Merged in feature/reset-licenses (pull request #89)
Feature/reset licenses Approved-by: Ramon Wenger
This commit is contained in:
commit
19f44483f1
|
|
@ -77,7 +77,7 @@ class UserSettingAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(License)
|
@admin.register(License)
|
||||||
class LicenseAdmin(admin.ModelAdmin):
|
class LicenseAdmin(admin.ModelAdmin):
|
||||||
list_display = ('licensee', 'for_role', 'expire_date', 'isbn')
|
list_display = ('licensee', 'for_role', 'expire_date', 'isbn', 'hep_created_at')
|
||||||
list_filter = ('licensee', 'expire_date')
|
list_filter = ('licensee', 'expire_date')
|
||||||
raw_id_fields = ('licensee',)
|
raw_id_fields = ('licensee',)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ MYSKILLBOX_LICENSES = {
|
||||||
},
|
},
|
||||||
"978-3-0355-1860-3": {
|
"978-3-0355-1860-3": {
|
||||||
'edition': STUDENT_KEY,
|
'edition': STUDENT_KEY,
|
||||||
'duration': 455,
|
'duration': 365,
|
||||||
'name': 'Student 1 year'
|
'name': 'Student 1 year'
|
||||||
},
|
},
|
||||||
"978-3-0355-1862-7": {
|
"978-3-0355-1862-7": {
|
||||||
|
|
@ -24,7 +24,7 @@ MYSKILLBOX_LICENSES = {
|
||||||
},
|
},
|
||||||
"978-3-0355-1823-8": {
|
"978-3-0355-1823-8": {
|
||||||
'edition': TEACHER_KEY,
|
'edition': TEACHER_KEY,
|
||||||
'duration': 455,
|
'duration': 365,
|
||||||
'name': 'Teacher 1 year'
|
'name': 'Teacher 1 year'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from users.models import License
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = """
|
||||||
|
Move raw data from new API to own field on License model
|
||||||
|
"""
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--dry-run', action='store_true', dest='dry_run', default=False, help='Make dry-run')
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
|
for license in License.objects.filter(new_api_raw=''):
|
||||||
|
if license.is_old_api():
|
||||||
|
continue
|
||||||
|
|
||||||
|
license.new_api_raw = license.raw
|
||||||
|
license.raw = ''
|
||||||
|
|
||||||
|
print(f'Moving raw to new API field for licenseID {license.id}')
|
||||||
|
|
||||||
|
if not options['dry_run']:
|
||||||
|
license.save()
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from users.licenses import MYSKILLBOX_LICENSES
|
||||||
|
from users.models import License, NO_DATE
|
||||||
|
|
||||||
|
YEARLY_ISBNS = ["978-3-0355-1860-3", "978-3-0355-1823-8"]
|
||||||
|
LONGER_DURATION = 455
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = """
|
||||||
|
Reset licenses that have a duration of one year
|
||||||
|
Uses the duration of MYSKILLBOX_LICENSES as reference.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--dry-run', action='store_true', dest='dry_run', default=False, help='Make dry-run')
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
today = now().date()
|
||||||
|
|
||||||
|
for license in License.objects.filter(expire_date__gte=today):
|
||||||
|
isbn = license.get_hep_isbn()
|
||||||
|
if isbn not in YEARLY_ISBNS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
standard_duration = MYSKILLBOX_LICENSES[isbn]['duration'] # use isbn from hep due to our bug
|
||||||
|
start_date = license.get_hep_start_date()
|
||||||
|
duration_in_days = (license.expire_date - start_date).days
|
||||||
|
if duration_in_days == standard_duration:
|
||||||
|
continue
|
||||||
|
|
||||||
|
user_join_delta = (license.expire_date - license.licensee.date_joined.date()).days
|
||||||
|
|
||||||
|
if self._is_change_candidate(user_join_delta, duration_in_days, start_date):
|
||||||
|
self._update_expiry_date(license, standard_duration, options['dry_run'])
|
||||||
|
|
||||||
|
def _is_change_candidate(self, user_join_delta: int, duration_in_days: int, start_date: date) -> bool:
|
||||||
|
"""
|
||||||
|
user_join_delta == LONGER_DURATION: user joined the same number of days as the old duration was
|
||||||
|
duration_in_days == LONGER_DURATION: delta from start date and expiry date is the same as the old duration
|
||||||
|
start_date == NO_DATE: license does not have a start date -> was created after moving to new api as
|
||||||
|
such it the longer duration was set as duration
|
||||||
|
"""
|
||||||
|
return user_join_delta == LONGER_DURATION or duration_in_days == LONGER_DURATION \
|
||||||
|
or start_date == NO_DATE
|
||||||
|
|
||||||
|
def _update_expiry_date(self, license: License, standard_duration: int, dry_run: bool):
|
||||||
|
new_expiry_date = license.expire_date - timedelta(LONGER_DURATION - standard_duration)
|
||||||
|
user_expire_date = license.licensee.license_expiry_date
|
||||||
|
|
||||||
|
if user_expire_date == license.expire_date:
|
||||||
|
print(f'Resetting user expire_date for userID {license.licensee.id} from'
|
||||||
|
f' {license.licensee.license_expiry_date} to {new_expiry_date}')
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
license.licensee.license_expiry_date = new_expiry_date
|
||||||
|
license.licensee.save()
|
||||||
|
|
||||||
|
print(f'Resetting expiry date for license ID {license.id} ({license.expire_date}) to {new_expiry_date}')
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
license.expire_date = new_expiry_date
|
||||||
|
license.save()
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from users.models import License
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = """
|
||||||
|
Overwrites the License model's IBAN with the IBAN from the hep-JSON
|
||||||
|
"""
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--dry-run', action='store_true', dest='dry_run', default=False, help='Make dry-run')
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
|
for license in License.objects.all():
|
||||||
|
isbn = license.get_hep_isbn()
|
||||||
|
|
||||||
|
if isbn == license.isbn:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f'Setting ISBN for licenseID {license.id} from {license.isbn} to {isbn}')
|
||||||
|
|
||||||
|
if not options['dry_run']:
|
||||||
|
license.isbn = isbn
|
||||||
|
license.save()
|
||||||
|
|
@ -147,14 +147,16 @@ class LicenseManager(models.Manager):
|
||||||
else:
|
else:
|
||||||
user_role = Role.objects.get_default_student_role()
|
user_role = Role.objects.get_default_student_role()
|
||||||
|
|
||||||
new_license = self._create_license_for_role(licensee, expiry_date, raw, user_role, order_id)
|
new_license = self._create_license_for_role(licensee, expiry_date, raw, user_role, order_id, isbn,
|
||||||
|
activation_date)
|
||||||
new_license.licensee.license_expiry_date = new_license.expire_date
|
new_license.licensee.license_expiry_date = new_license.expire_date
|
||||||
new_license.licensee.save()
|
new_license.licensee.save()
|
||||||
|
|
||||||
return new_license
|
return new_license
|
||||||
|
|
||||||
def _create_license_for_role(self, licensee, expiry_date, raw, role, order_id):
|
def _create_license_for_role(self, licensee, expiry_date, raw, role, order_id, isbn, activation_date):
|
||||||
return self.create(licensee=licensee, expire_date=expiry_date, raw=raw, for_role=role, order_id=order_id)
|
return self.create(licensee=licensee, expire_date=expiry_date, new_api_raw=raw, for_role=role, order_id=order_id,
|
||||||
|
isbn=isbn, hep_created_at=activation_date)
|
||||||
|
|
||||||
def get_active_license_for_user(self, user):
|
def get_active_license_for_user(self, user):
|
||||||
licenses = self.filter(licensee=user, expire_date__gte=timezone.now()).order_by('-expire_date')
|
licenses = self.filter(licensee=user, expire_date__gte=timezone.now()).order_by('-expire_date')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.2.24 on 2021-09-22 05:50
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0027_auto_20210414_2116'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='license',
|
||||||
|
name='hep_created_at',
|
||||||
|
field=models.DateTimeField(default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=utc)),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='license',
|
||||||
|
name='new_api_raw',
|
||||||
|
field=models.TextField(default=''),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 2.2.24 on 2021-09-22 05:50
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from users.models import AWARE_NO_DATETIME
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0028_auto_20210922_0550'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='license',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=AWARE_NO_DATETIME),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
import json
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta, MINYEAR
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
@ -17,6 +18,9 @@ from users.licenses import MYSKILLBOX_LICENSES
|
||||||
from users.managers import LicenseManager, RoleManager, UserManager, UserRoleManager
|
from users.managers import LicenseManager, RoleManager, UserManager, UserRoleManager
|
||||||
|
|
||||||
DEFAULT_SCHOOL_ID = 1
|
DEFAULT_SCHOOL_ID = 1
|
||||||
|
NO_DATE = date(MINYEAR, 1, 1) # date to tag licenses without date
|
||||||
|
NO_DATETIME = datetime.combine(NO_DATE, datetime.min.time())
|
||||||
|
AWARE_NO_DATETIME = make_aware(NO_DATETIME)
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
|
|
@ -305,6 +309,9 @@ class License(models.Model):
|
||||||
raw = models.TextField(default='')
|
raw = models.TextField(default='')
|
||||||
isbn = models.CharField(max_length=50, blank=False, null=False,
|
isbn = models.CharField(max_length=50, blank=False, null=False,
|
||||||
default=list(MYSKILLBOX_LICENSES.keys())[0]) # student license
|
default=list(MYSKILLBOX_LICENSES.keys())[0]) # student license
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
hep_created_at = models.DateTimeField(default=AWARE_NO_DATETIME)
|
||||||
|
new_api_raw = models.TextField(default='')
|
||||||
|
|
||||||
objects = LicenseManager()
|
objects = LicenseManager()
|
||||||
|
|
||||||
|
|
@ -315,6 +322,41 @@ class License(models.Model):
|
||||||
date = make_aware(datetime(self.expire_date.year, self.expire_date.month, self.expire_date.day))
|
date = make_aware(datetime(self.expire_date.year, self.expire_date.month, self.expire_date.day))
|
||||||
return License.is_product_active(date, self.isbn)
|
return License.is_product_active(date, self.isbn)
|
||||||
|
|
||||||
|
def _read_as_json(self, raw_string: str) -> dict:
|
||||||
|
return json.loads(raw_string.replace("'", '"').replace('True', 'true').replace('False', 'false')
|
||||||
|
.replace('None', 'null'))
|
||||||
|
|
||||||
|
def get_hep_start_date(self) -> date:
|
||||||
|
hep_data = self._read_as_json(self.raw)
|
||||||
|
if 'updated_at' in hep_data: # old key from Magento. Format: 2020-06-22 18:45:13
|
||||||
|
date_string = hep_data['created_at']
|
||||||
|
return datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S').date()
|
||||||
|
|
||||||
|
return NO_DATE
|
||||||
|
|
||||||
|
def get_hep_isbn(self) -> str:
|
||||||
|
hep_data = self._read_as_json(self.raw)
|
||||||
|
if self._is_old_api(hep_data):
|
||||||
|
return hep_data['sku']
|
||||||
|
elif self._is_new_api(hep_data):
|
||||||
|
return hep_data['isbn']
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def is_new_api(self) -> bool:
|
||||||
|
hep_data = self._read_as_json(self.raw)
|
||||||
|
return self._is_new_api(hep_data)
|
||||||
|
|
||||||
|
def is_old_api(self) -> bool:
|
||||||
|
hep_data = self._read_as_json(self.raw)
|
||||||
|
return self._is_old_api(hep_data)
|
||||||
|
|
||||||
|
def _is_new_api(self, hep_data: dict) -> bool:
|
||||||
|
return 'isbn' in hep_data
|
||||||
|
|
||||||
|
def _is_old_api(self, hep_data: dict) -> bool:
|
||||||
|
return 'sku' in hep_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_product_active(expiry_date, isbn):
|
def is_product_active(expiry_date, isbn):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue