Skip to content

Commit 1aa7f71

Browse files
committed
Add models for insights and migration script
Signed-off-by: Sampurna Pyne <sampurnapyne1710@gmail.com>
1 parent 4c7f630 commit 1aa7f71

3 files changed

Lines changed: 290 additions & 0 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Generated by Django 5.2.11 on 2026-07-23 21:56
2+
3+
import django.contrib.postgres.fields
4+
import django.db.models.deletion
5+
from django.db import migrations
6+
from django.db import models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
initial = True
12+
13+
dependencies = []
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="DailySnapshot",
18+
fields=[
19+
(
20+
"id",
21+
models.AutoField(
22+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
23+
),
24+
),
25+
("created_at", models.DateTimeField(auto_now_add=True)),
26+
],
27+
options={
28+
"ordering": ["-created_at"],
29+
"get_latest_by": "created_at",
30+
},
31+
),
32+
migrations.CreateModel(
33+
name="PackageInsight",
34+
fields=[
35+
(
36+
"id",
37+
models.AutoField(
38+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
39+
),
40+
),
41+
("package", models.CharField(max_length=50)),
42+
("total_packages", models.IntegerField(default=0)),
43+
(
44+
"snapshot",
45+
models.ForeignKey(
46+
on_delete=django.db.models.deletion.CASCADE,
47+
related_name="package_insights",
48+
to="insights.dailysnapshot",
49+
),
50+
),
51+
],
52+
),
53+
migrations.CreateModel(
54+
name="PackageCWEInsight",
55+
fields=[
56+
(
57+
"id",
58+
models.AutoField(
59+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
60+
),
61+
),
62+
("cwe_id", models.CharField(max_length=50)),
63+
("count", models.IntegerField()),
64+
(
65+
"package_insight",
66+
models.ForeignKey(
67+
on_delete=django.db.models.deletion.CASCADE,
68+
related_name="cwes",
69+
to="insights.packageinsight",
70+
),
71+
),
72+
],
73+
),
74+
migrations.CreateModel(
75+
name="PackageNameInsight",
76+
fields=[
77+
(
78+
"id",
79+
models.AutoField(
80+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
81+
),
82+
),
83+
("name", models.CharField(max_length=255)),
84+
("count", models.IntegerField()),
85+
(
86+
"package_insight",
87+
models.ForeignKey(
88+
on_delete=django.db.models.deletion.CASCADE,
89+
related_name="names",
90+
to="insights.packageinsight",
91+
),
92+
),
93+
],
94+
),
95+
migrations.CreateModel(
96+
name="SeverityInsight",
97+
fields=[
98+
(
99+
"id",
100+
models.AutoField(
101+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
102+
),
103+
),
104+
(
105+
"buckets",
106+
django.contrib.postgres.fields.ArrayField(
107+
base_field=models.IntegerField(),
108+
default=list,
109+
help_text="Scores mapped to buckets 0-10 (e.g. 0.0-0.9 -> index 0)",
110+
size=10,
111+
),
112+
),
113+
(
114+
"snapshot",
115+
models.OneToOneField(
116+
on_delete=django.db.models.deletion.CASCADE,
117+
related_name="severity_insight",
118+
to="insights.dailysnapshot",
119+
),
120+
),
121+
],
122+
),
123+
migrations.CreateModel(
124+
name="ImporterInsight",
125+
fields=[
126+
(
127+
"id",
128+
models.AutoField(
129+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
130+
),
131+
),
132+
("importer", models.CharField(max_length=100)),
133+
("total_advisories", models.IntegerField(default=0)),
134+
("advisories_with_packages", models.IntegerField(default=0)),
135+
("advisories_with_ghost_packages", models.IntegerField(default=0)),
136+
("advisories_with_kev", models.IntegerField(default=0)),
137+
("advisories_with_metasploit", models.IntegerField(default=0)),
138+
("advisories_with_exploitdb", models.IntegerField(default=0)),
139+
(
140+
"snapshot",
141+
models.ForeignKey(
142+
on_delete=django.db.models.deletion.CASCADE,
143+
related_name="importer_insights",
144+
to="insights.dailysnapshot",
145+
),
146+
),
147+
],
148+
options={
149+
"constraints": [
150+
models.UniqueConstraint(
151+
fields=("snapshot", "importer"), name="unique_snapshot_importer"
152+
)
153+
],
154+
},
155+
),
156+
migrations.AddConstraint(
157+
model_name="packageinsight",
158+
constraint=models.UniqueConstraint(
159+
fields=("snapshot", "package"), name="unique_snapshot_package"
160+
),
161+
),
162+
migrations.AddConstraint(
163+
model_name="packagecweinsight",
164+
constraint=models.UniqueConstraint(
165+
fields=("package_insight", "cwe_id"), name="unique_package_cwe"
166+
),
167+
),
168+
migrations.AddConstraint(
169+
model_name="packagenameinsight",
170+
constraint=models.UniqueConstraint(
171+
fields=("package_insight", "name"), name="unique_package_name"
172+
),
173+
),
174+
]

‎insights/migrations/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#

‎insights/models.py‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
from dataclasses import dataclass
10+
from typing import Any
11+
from typing import Callable
12+
from typing import Dict
13+
from typing import Optional
14+
15+
from django.contrib.postgres.fields import ArrayField
16+
from django.db import models
17+
18+
19+
class DailySnapshot(models.Model):
20+
created_at = models.DateTimeField(auto_now_add=True)
21+
22+
class Meta:
23+
get_latest_by = "created_at"
24+
ordering = ["-created_at"]
25+
26+
27+
class PackageInsight(models.Model):
28+
snapshot = models.ForeignKey(
29+
DailySnapshot, related_name="package_insights", on_delete=models.CASCADE
30+
)
31+
package = models.CharField(max_length=50)
32+
total_packages = models.IntegerField(default=0)
33+
34+
class Meta:
35+
constraints = [
36+
models.UniqueConstraint(fields=["snapshot", "package"], name="unique_snapshot_package")
37+
]
38+
39+
40+
class PackageNameInsight(models.Model):
41+
package_insight = models.ForeignKey(
42+
PackageInsight, related_name="names", on_delete=models.CASCADE
43+
)
44+
name = models.CharField(max_length=255)
45+
count = models.IntegerField()
46+
47+
class Meta:
48+
constraints = [
49+
models.UniqueConstraint(fields=["package_insight", "name"], name="unique_package_name")
50+
]
51+
52+
53+
class PackageCWEInsight(models.Model):
54+
package_insight = models.ForeignKey(
55+
PackageInsight, related_name="cwes", on_delete=models.CASCADE
56+
)
57+
cwe_id = models.CharField(max_length=50)
58+
count = models.IntegerField()
59+
60+
class Meta:
61+
constraints = [
62+
models.UniqueConstraint(fields=["package_insight", "cwe_id"], name="unique_package_cwe")
63+
]
64+
65+
66+
class SeverityInsight(models.Model):
67+
snapshot = models.OneToOneField(
68+
DailySnapshot, related_name="severity_insight", on_delete=models.CASCADE
69+
)
70+
buckets = ArrayField(
71+
models.IntegerField(),
72+
size=10,
73+
default=list,
74+
help_text="Scores mapped to buckets 0-10 (e.g. 0.0-0.9 -> index 0)",
75+
)
76+
77+
78+
class ImporterInsight(models.Model):
79+
snapshot = models.ForeignKey(
80+
DailySnapshot, related_name="importer_insights", on_delete=models.CASCADE
81+
)
82+
importer = models.CharField(max_length=100)
83+
total_advisories = models.IntegerField(default=0)
84+
advisories_with_packages = models.IntegerField(default=0)
85+
advisories_with_ghost_packages = models.IntegerField(default=0)
86+
advisories_with_kev = models.IntegerField(default=0)
87+
advisories_with_metasploit = models.IntegerField(default=0)
88+
advisories_with_exploitdb = models.IntegerField(default=0)
89+
90+
class Meta:
91+
constraints = [
92+
models.UniqueConstraint(
93+
fields=["snapshot", "importer"], name="unique_snapshot_importer"
94+
)
95+
]
96+
97+
98+
@dataclass(frozen=True)
99+
class GraphDefinition:
100+
101+
id: str
102+
title: str
103+
panel: str
104+
chart_type: str
105+
data_fn: Callable[[Dict[str, Any]], Dict[str, Any]]
106+
collect_fn: Optional[Callable] = None
107+
is_per_package: bool = False
108+
has_search: bool = False

0 commit comments

Comments
 (0)