Skip to content

Commit 507407d

Browse files
SalmanAshSKairinos
andauthored
Api models (#1)
* Start api models for contribution agreement (#82) * api models for contribution * change import sorting and format * fixture data for testing * change model fields * test string parsing * Migrate models * fixture data for testing * api models and tests files * fix import statements * fix code format * Fix static types * fix linting test file format * import fix * Check static code * import sort * update DateTime Field * Tests for models * Repository testing * fix class name * tests for models * correct formatting and comments * Change meta classes * Fix code format and imports * Delete all fruit files * Delete last fruit migration * Fix migration issue * change data fields * Apply changes after review * apply initial migrations * Fix contributor type error * Add verbose names * Fix import error * Apply changes from second review * Migrate models --------- Co-authored-by: Stefan Kairinos <[email protected]>
1 parent 81ac9d1 commit 507407d

21 files changed

+332
-216
lines changed
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[
2+
{
3+
"model": "api.agreementsignature",
4+
"pk": 1,
5+
"fields": {
6+
"contributor": 1,
7+
"agreement_id": "g3d3d3s8dgd3vc37232fef32232df3f3f31fgawf",
8+
"signed_at": "2024-01-02T12:00:00Z"
9+
}
10+
},
11+
{
12+
"model": "api.agreementsignature",
13+
"pk": 2,
14+
"fields": {
15+
"contributor": 2,
16+
"agreement_id": "g3d3d3s8dgd43vc37232fef0898df3f3f31fgawf",
17+
"signed_at": "2024-01-02T12:00:00Z"
18+
}
19+
},
20+
{
21+
"model": "api.agreementsignature",
22+
"pk": 3,
23+
"fields": {
24+
"contributor": 3,
25+
"agreement_id": "g379tuehr8dgd43vc37232fef0898df3f3f31fga",
26+
"signed_at": "2024-01-02T12:00:00Z"
27+
}
28+
}
29+
]

api/fixtures/contributors.json

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"model": "api.contributor",
4+
"pk": 1,
5+
"fields": {
6+
"email": "[email protected]",
7+
"name": "contributor1",
8+
"location": "Hatfield",
9+
"html_url": "https://github.com/contributor1",
10+
"avatar_url": "https://contributor1.github.io/gravatar-url-generator/#/"
11+
}
12+
},
13+
{
14+
"model": "api.contributor",
15+
"pk": 2,
16+
"fields": {
17+
"email": "[email protected]",
18+
"name": "contributor2",
19+
"location": "Hatfield",
20+
"html_url": "https://github.com/contributor2",
21+
"avatar_url": "https://contributor2.github.io/gravatar-url-generator/#/"
22+
}
23+
},
24+
{
25+
"model": "api.contributor",
26+
"pk": 3,
27+
"fields": {
28+
"email": "[email protected]",
29+
"name": "contributor3",
30+
"location": "Hatfield",
31+
"html_url": "https://github.com/contributor3",
32+
"avatar_url": "https://contributor3.github.io/gravatar-url-generator/#/"
33+
}
34+
}
35+
]

api/fixtures/fruits.json

-29
This file was deleted.

api/fixtures/repositories.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"model": "api.repository",
4+
"pk": 1,
5+
"fields": {
6+
"contributor": 1,
7+
"points": 10,
8+
"gh_id": "10274252"
9+
}
10+
},
11+
{
12+
"model": "api.repository",
13+
"pk": 2,
14+
"fields": {
15+
"contributor": 2,
16+
"points": 20,
17+
"gh_id": "102097552"
18+
19+
}
20+
},
21+
{
22+
"model": "api.repository",
23+
"pk": 3,
24+
"fields": {
25+
"contributor": 3,
26+
"points": 30,
27+
"gh_id": "890732552"
28+
}
29+
}
30+
]

api/migrations/0001_initial.py

+38-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Generated by Django 3.2.25 on 2024-07-02 15:57
1+
# Generated by Django 3.2.25 on 2024-07-12 15:33
22

33
from django.db import migrations, models
4+
import django.db.models.deletion
45

56

67
class Migration(migrations.Migration):
@@ -12,16 +13,46 @@ class Migration(migrations.Migration):
1213

1314
operations = [
1415
migrations.CreateModel(
15-
name='Fruit',
16+
name='Contributor',
17+
fields=[
18+
('id', models.IntegerField(help_text="The contributor's GitHub user-ID.", primary_key=True, serialize=False)),
19+
('email', models.EmailField(max_length=254, verbose_name='email')),
20+
('name', models.TextField(verbose_name='name')),
21+
('location', models.TextField(verbose_name='location')),
22+
('html_url', models.TextField(verbose_name='html url')),
23+
('avatar_url', models.TextField(verbose_name='avatar url')),
24+
],
25+
options={
26+
'verbose_name': 'contributor',
27+
'verbose_name_plural': 'contributors',
28+
},
29+
),
30+
migrations.CreateModel(
31+
name='Repository',
32+
fields=[
33+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
34+
('gh_id', models.IntegerField(help_text='Github ID of the repo a contributor has contributed to.', verbose_name='GitHub ID')),
35+
('points', models.IntegerField(default=0, help_text='Story points the contributor closed for this repository.', verbose_name='points')),
36+
('contributor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.contributor')),
37+
],
38+
options={
39+
'verbose_name': 'repository',
40+
'verbose_name_plural': 'repositories',
41+
'unique_together': {('contributor', 'gh_id')},
42+
},
43+
),
44+
migrations.CreateModel(
45+
name='AgreementSignature',
1646
fields=[
1747
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18-
('name', models.CharField(max_length=50, verbose_name='name')),
19-
('is_citrus', models.BooleanField(verbose_name='is citrus')),
20-
('expires_on', models.DateField(verbose_name='expires on')),
48+
('agreement_id', models.CharField(help_text='Commit ID of the contribution agreement in workspace.', max_length=40, verbose_name='agreement id')),
49+
('signed_at', models.DateTimeField(verbose_name='signed at')),
50+
('contributor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.contributor')),
2151
],
2252
options={
23-
'verbose_name': 'fruit',
24-
'verbose_name_plural': 'fruits',
53+
'verbose_name': 'agreement signature',
54+
'verbose_name_plural': 'agreement signatures',
55+
'unique_together': {('contributor', 'agreement_id')},
2556
},
2657
),
2758
]

api/models/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
Created on 02/07/2024 at 11:57:31(+01:00).
44
"""
55

6-
from .fruit import Fruit
6+
from .agreement_signature import AgreementSignature
7+
from .contributor import Contributor
8+
from .repository import Repository

api/models/agreement_signature.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
© Ocado Group
3+
Created on 08/07/2024 at 10:48:44(+01:00).
4+
"""
5+
6+
import typing as t
7+
8+
from django.db import models
9+
from django.utils.translation import gettext_lazy as _
10+
11+
from .contributor import Contributor
12+
13+
if t.TYPE_CHECKING:
14+
from django_stubs_ext.db.models import TypedModelMeta # pragma: no cover
15+
else:
16+
TypedModelMeta = object
17+
18+
19+
class AgreementSignature(models.Model):
20+
"""Signature of a contributor signing the agreement"""
21+
22+
contributor_id: int
23+
contributor = models.ForeignKey(Contributor, on_delete=models.CASCADE)
24+
25+
agreement_id = models.CharField(
26+
_("agreement id"),
27+
max_length=40,
28+
help_text=_("Commit ID of the contribution agreement in workspace."),
29+
)
30+
signed_at = models.DateTimeField(_("signed at"))
31+
32+
class Meta(TypedModelMeta):
33+
unique_together = ["contributor", "agreement_id"]
34+
verbose_name = _("agreement signature")
35+
verbose_name_plural = _("agreement signatures")
36+
37+
def __str__(self):
38+
cont = f"Contributor {self.contributor.pk} signed"
39+
repo = f"{self.agreement_id[:7]} at {self.signed_at}"
40+
return f"{cont} {repo}"
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
© Ocado Group
3+
Created on 09/07/2024 at 11:43:42(+01:00).
4+
"""
5+
6+
from codeforlife.tests import ModelTestCase
7+
8+
from .agreement_signature import AgreementSignature
9+
10+
11+
class TestAgreementSignature(ModelTestCase[AgreementSignature]):
12+
"""Test the AgreementSignature Model"""
13+
14+
fixtures = ["contributors", "agreement_signatures"]
15+
16+
def setUp(self):
17+
self.agreement_signature = AgreementSignature.objects.get(pk=1)
18+
19+
def test_str(self):
20+
"""
21+
Parsing an agreement-signature instance to a string
22+
that returns the contributor's primary key,
23+
the first 7 characters of the agreement's commit ID
24+
and the timestamp of when the agreement was signed.
25+
"""
26+
commit_id = self.agreement_signature.agreement_id[:7]
27+
time = self.agreement_signature.signed_at
28+
cont = f"Contributor {self.agreement_signature.contributor.pk} signed"
29+
repo = f"{commit_id} at {time}"
30+
expected_str = f"{cont} {repo}"
31+
assert str(self.agreement_signature) == expected_str

api/models/contributor.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
© Ocado Group
3+
Created on 05/07/2024 at 16:18:48(+01:00).
4+
"""
5+
6+
import typing as t
7+
8+
from django.db import models
9+
from django.utils.translation import gettext_lazy as _
10+
11+
if t.TYPE_CHECKING:
12+
from django_stubs_ext.db.models import TypedModelMeta # pragma: no cover
13+
else:
14+
TypedModelMeta = object
15+
16+
17+
class Contributor(models.Model):
18+
"""A contributor that contributes to a repo"""
19+
20+
id = models.IntegerField(
21+
primary_key=True, help_text=_("The contributor's GitHub user-ID.")
22+
)
23+
email = models.EmailField(_("email"))
24+
name = models.TextField(_("name"))
25+
location = models.TextField(_("location"))
26+
html_url = models.TextField(_("html url"))
27+
avatar_url = models.TextField(_("avatar url"))
28+
29+
class Meta(TypedModelMeta):
30+
verbose_name = _("contributor")
31+
verbose_name_plural = _("contributors")
32+
33+
def __str__(self):
34+
return f"{self.name} <{self.email}>"

api/models/contributor_test.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
© Ocado Group
3+
Created on 09/07/2024 at 09:39:50(+01:00).
4+
"""
5+
6+
from codeforlife.tests import ModelTestCase
7+
8+
from .contributor import Contributor
9+
10+
11+
class TestContributor(ModelTestCase[Contributor]):
12+
"""Test the Contributor Model"""
13+
14+
fixtures = ["contributors"]
15+
16+
def setUp(self):
17+
self.contributor = Contributor.objects.get(pk=1)
18+
19+
def test_str(self):
20+
"""
21+
Parsing a contributor instance to a string returns its name and email.
22+
"""
23+
name = self.contributor.name
24+
email = self.contributor.email
25+
assert str(self.contributor) == f"{name} <{email}>"

api/models/fruit.py

-32
This file was deleted.

api/models/fruit_test.py

-28
This file was deleted.

0 commit comments

Comments
 (0)