From 7f9f579d53a4ec818682419116d98f44adf4b919 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 23 Jan 2018 16:32:56 +0300 Subject: [PATCH] user out_key --- access/admin.py | 8 +- access/middleware.py | 27 +++++-- access/migrations/0003_useractivity.py | 31 ++++++++ access/models/other.py | 16 +++- access/models/user.py | 3 +- access/serializers.py | 18 ++--- access/views.py | 4 +- courses/serializers.py | 2 +- gitlab_service/__init__.py | 0 gitlab_service/admin.py | 5 -- gitlab_service/api.py | 88 -------------------- gitlab_service/apps.py | 7 -- gitlab_service/gitlab_wrapper.py | 106 ------------------------- gitlab_service/migrations/__init__.py | 0 gitlab_service/models.py | 21 ----- gitlab_service/tests.py | 4 - gitlab_service/views.py | 5 -- lms/settings.py | 1 + storage/api.py | 3 +- storage/models.py | 5 +- storage/views.py | 15 ++++ 21 files changed, 104 insertions(+), 265 deletions(-) create mode 100644 access/migrations/0003_useractivity.py delete mode 100644 gitlab_service/__init__.py delete mode 100644 gitlab_service/admin.py delete mode 100644 gitlab_service/api.py delete mode 100644 gitlab_service/apps.py delete mode 100644 gitlab_service/gitlab_wrapper.py delete mode 100644 gitlab_service/migrations/__init__.py delete mode 100644 gitlab_service/models.py delete mode 100644 gitlab_service/tests.py delete mode 100644 gitlab_service/views.py diff --git a/access/admin.py b/access/admin.py index 6a51244..278435d 100755 --- a/access/admin.py +++ b/access/admin.py @@ -1,7 +1,6 @@ from django.contrib import admin -from access.models.other import Invite, Account, ResetPassword -from access.models.progress import ProgressLesson -from access.models import Progress +from access.models.other import Invite, Account, ResetPassword, UserActivity +from access.models.progress import ProgressLesson, Progress from access.models.user import User admin.site.register(User) @@ -9,4 +8,5 @@ admin.site.register(Account) admin.site.register(Progress) admin.site.register(Invite) admin.site.register(ResetPassword) -admin.site.register(ProgressLesson) \ No newline at end of file +admin.site.register(ProgressLesson) +admin.site.register(UserActivity) diff --git a/access/middleware.py b/access/middleware.py index 8644bad..2aa5500 100644 --- a/access/middleware.py +++ b/access/middleware.py @@ -1,6 +1,7 @@ from django.http import HttpResponseForbidden from django.http import QueryDict +from access.models.other import UserActivity import json @@ -8,16 +9,15 @@ class CheckPerm(object): @staticmethod def process_request(request): if len(request.path) > 6 and \ - ('/admin' == request.path[:6] - or'/analy' == request.path[:6]): - #or "/api/v" == request.path[:6]): + ('/admin' == request.path[:6] + or '/analy' == request.path[:6]): + # or "/api/v" == request.path[:6]): if not request.user.is_authenticated(): return HttpResponseForbidden() if not (request.user.in_role == "M" or request.user.in_role == "S" or request.user.in_role == "A" or request.user.is_admin): - return HttpResponseForbidden() @@ -33,4 +33,21 @@ class RequestToApi(object): request.JSON = q_data if request.method == 'POST' or request.method == 'DELETE': # TODO или выпилить или в зависимость от settings - setattr(request, '_dont_enforce_csrf_checks', True) \ No newline at end of file + setattr(request, '_dont_enforce_csrf_checks', True) + + +class UpdateActivity(object): + @staticmethod + def process_request(request): + if not request.user.is_anonymous: + user_activity = UserActivity.objects.get(owner=request.user) + ip = request.META.get('REMOTE_ADDR', None) + if user_activity.ip_list is None: + user_activity.ip_list = [] + + if not ip is None: + new_list = list(user_activity.ip_list) + new_list.append(ip) + user_activity.ip_list = list(set(new_list)) + + user_activity.save() diff --git a/access/migrations/0003_useractivity.py b/access/migrations/0003_useractivity.py new file mode 100644 index 0000000..b87c032 --- /dev/null +++ b/access/migrations/0003_useractivity.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-01-23 10:11 +from __future__ import unicode_literals + +from django.conf import settings +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0002_init_group'), + ] + + operations = [ + migrations.CreateModel( + name='UserActivity', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ip_list', django.contrib.postgres.fields.ArrayField(base_field=models.GenericIPAddressField(editable=False, verbose_name='Ip адресс'), default=[], size=None)), + ('last_request', models.DateTimeField(auto_now=True, verbose_name='Был в сети')), + ('owner', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Пользовательская активность', + 'verbose_name_plural': 'Пользовательская активность', + }, + ), + ] diff --git a/access/models/other.py b/access/models/other.py index a3fdc7f..bdaa3bb 100644 --- a/access/models/other.py +++ b/access/models/other.py @@ -1,6 +1,7 @@ from django.conf import settings from django.db import models from phonenumber_field.modelfields import PhoneNumberField +from django.contrib.postgres.fields import ArrayField class Invite(models.Model): @@ -21,13 +22,26 @@ class ResetPassword(Invite): verbose_name_plural = "Запросы на сброс пароля" +class UserActivity(models.Model): + owner = models.OneToOneField(to=settings.AUTH_USER_MODEL) + ip_list = ArrayField(models.GenericIPAddressField(verbose_name="Ip адресс", editable=False), default=[]) + last_request = models.DateTimeField(verbose_name="Был в сети", auto_now=True) + + def __str__(self): + return self.owner.email + + class Meta: + verbose_name = 'Пользовательская активность' + verbose_name_plural = 'Пользовательская активность' + + class Account(models.Model): GENDER_CHOICES = ( (0, 'undefined'), (1, 'male'), (2, 'female'), ) - b_day = models.DateField(blank=True, null=True) # TODO birth_day + b_day = models.DateField(blank=True, null=True) city = models.CharField(max_length=63, null=True, blank=True) gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0) owner = models.OneToOneField(to=settings.AUTH_USER_MODEL, null=True) diff --git a/access/models/user.py b/access/models/user.py index 7fdb720..af2cf77 100644 --- a/access/models/user.py +++ b/access/models/user.py @@ -9,7 +9,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from access.models.other import Invite, Account +from access.models.other import Invite, Account, UserActivity from lms.global_decorators import transaction_decorator @@ -58,6 +58,7 @@ class CustomUserManager(BaseUserManager): user.save(using=self._db) Account.objects.create(owner=user) + UserActivity.objects.create(owner=user) if role_list: for group in role_list: diff --git a/access/serializers.py b/access/serializers.py index 54dc988..b213ea4 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -99,10 +99,12 @@ class UserProfileSerializer(serializers.ModelSerializer): class UserSearchSerializer(serializers.ModelSerializer): pay = serializers.SerializerMethodField() phone = serializers.SerializerMethodField() + photo = serializers.SerializerMethodField() + last_request = serializers.SerializerMethodField() class Meta: model = get_user_model() - fields = ('out_key', 'email', 'first_name', 'last_name', 'phone', 'pay') + fields = ('photo', 'out_key', 'email', 'first_name', 'last_name', 'phone', 'pay', 'last_request') @staticmethod def get_phone(self): @@ -112,14 +114,10 @@ class UserSearchSerializer(serializers.ModelSerializer): def get_pay(self): return sum([i.get_full_price() for i in self.bill_user.all()]) - -class UserMiniSerializer(serializers.ModelSerializer): - photo = serializers.SerializerMethodField() - - class Meta: - model = get_user_model() - fields = ('photo', 'first_name', 'last_name') - @staticmethod def get_photo(self): - return self.account.photo.url \ No newline at end of file + return self.account.photo.url + + @staticmethod + def get_last_request(self): + return self.useractivity.last_request diff --git a/access/views.py b/access/views.py index 5ff692d..9f598c7 100644 --- a/access/views.py +++ b/access/views.py @@ -16,7 +16,7 @@ from rest_framework.views import APIView from access.models.other import Invite, ResetPassword from access.models.progress import ProgressLesson from access.models import Progress -from access.serializers import UserSelfSerializer, UserSearchSerializer, ProgressLessonSerializer, UserMiniSerializer +from access.serializers import UserSelfSerializer, UserSearchSerializer, ProgressLessonSerializer class TeacherListView(APIView): @@ -324,6 +324,6 @@ class MinUserView(APIView): @staticmethod def get(request, out_key): try: - return Response(UserMiniSerializer(get_user_model().objects.get(out_key=out_key)).data, status=200) + return Response(UserSearchSerializer(get_user_model().objects.get(out_key=out_key)).data, status=200) except get_user_model().DoesNotExist: return Response("User not found", status=404) diff --git a/courses/serializers.py b/courses/serializers.py index 3c9d26f..7d71cd8 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -26,7 +26,7 @@ class LessonSerializer(MiniLessonSerializer): class Meta: model = Lesson - exclude = ('id', 'topic', 'free') + exclude = ('id', 'topic', 'free', 'key') class CourseInitSerializer(serializers.ModelSerializer): diff --git a/gitlab_service/__init__.py b/gitlab_service/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gitlab_service/admin.py b/gitlab_service/admin.py deleted file mode 100644 index ab35bfd..0000000 --- a/gitlab_service/admin.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.contrib import admin - -# Register your models here. diff --git a/gitlab_service/api.py b/gitlab_service/api.py deleted file mode 100644 index 9e05fa1..0000000 --- a/gitlab_service/api.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- - -# Сервис может -# интерфейс учителя: -# - логин -# - выбор курса для работы -# - привязка (или создание) репы для курса (можно через веб) -# - прописывание соответствий ДЗ курса <-> путь в репе (можно через веб) -# - clone всех реп учеников данного курса и базовой репы -# - pull всех изменений реп студентов данного курса -# - push всех новых коммитов учителя в репы студентов (+ save point опционально) -# - changes по всем репам студентов данного курса с момента последнего save point (можно через веб) -# - save point состояния реп студентов (можно через веб) -# - разнесение изменений в базовой репе по репам студентов - -# соотвтествие путей в гите к курсам/домашкам храним у себя, какая домашка следующая для ученика - спрашиваем ЛМС - -# запросы к ЛМС -# - get по токену получить ИД учителя, емейл и имя -# - get по ИД учителя список его курсов (ИД, название) -# - get по ИД курса список всех опубликованных домашек (ИД, название) -# - get по ИД учителя и ИД курса список всех его студентов (ИД, емейл, имя) -# - get по ИД курса + ИД студента + ИД домашки дай следующую доамашку (ИД, название) -# - post ИД студента, ИД домашки - ДЗ принято - - -# запросы к сервису -# - get получение пользователя -# - get - -def register_user(email, name): - """ Создает пользователя в сервисе и возвращает токен пользователя для последующей работы """ - user_token = '...' - return user_token - - -def get_user(email): - """ Выбирает пользователя сервиса возвращает токен пользователя для последующей работы """ - user_token = '...' - return user_token - - -def create_git_user(user): - """ Создает пользователя в GitLab""" - pass - - -def get_users(emails): - """ Выбирает пользователей GitLab и возвращает токены для последующей работы """ - tokens = ['...', '...', '...', ] - return tokens - - -def create_repository(user, project_name): - """ Создает проект (репу) в GitLab и возвращает токен репу """ - repository_token = '...' - return repository_token - - -def get_repository(user, project_name): - """ Возвращает токен репы """ - repository_token = '...' - return repository_token - - -def make_user_project_master(user, project): - """ Делает пользователя мастером в проекте """ - pass - - -def copy_files_to_repository(base_repository, files_path, target_repository, autor): - """ Копирует файлы из базовой репы в целевую от имени автора """ - pass - - -def approve_homework(teacher, base_repository, student, target_repository, files_path): - """ Принять домашку у студента, копировать новые файлы """ - pass - - -def make_save_point(teacher, students): - """ зафиксировать точку в репах с принятыми домашками """ - pass - - -def get_last_changes(teacher, students, save_point=None): - """ показать последние изменения в репах учеников относительно save point (последнего, если None) """ - pass diff --git a/gitlab_service/apps.py b/gitlab_service/apps.py deleted file mode 100644 index 51bc2d4..0000000 --- a/gitlab_service/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.apps import AppConfig - - -class GitlabServiceConfig(AppConfig): - name = 'gitlab_service' diff --git a/gitlab_service/gitlab_wrapper.py b/gitlab_service/gitlab_wrapper.py deleted file mode 100644 index 514f33d..0000000 --- a/gitlab_service/gitlab_wrapper.py +++ /dev/null @@ -1,106 +0,0 @@ -# -*- coding: utf-8 -*- -import gitlab - - -class GitlabWrapperException(Exception): - pass - - -class GitlabWrapperFilesExists(GitlabWrapperException): - pass - - -class GitlabWrapper: - API_VERSION = '4' - - def __init__(self, url, token, ssl_verify=True): - self.url = url - self.token = token - self.gl = gitlab.Gitlab( - url=self.url, private_token=self.token, api_version=self.API_VERSION, ssl_verify=ssl_verify) - - def get_user(self, email): - exists_users = self.gl.users.list(search=email) - if exists_users: - return exists_users[0] - - def create_user(self, email, name): - username = email[:email.find('@')].replace('.', '_') - # TODO can contain only letters, digits, '_', '-' and '.'. - # TODO Cannot start with '-' or end in '.', '.git' or '.atom'." - exists_users = self.gl.users.list(username=username) - if len(exists_users): - username = username + '_{}'.format(len(exists_users) + 1) - user_data = dict( - email=email, - username=username, - name=name, - reset_password=True, - ) - new_user = self.gl.users.create(user_data) - return new_user - - def get_or_create_user(self, email, name): - user = self.get_user(email) - if user is None: - user = self.create_user(email, name) - return user - - def get_user_project(self, user, project_name): - try: - exists_project = self.gl.projects.get('{}/{}'.format(user.username, project_name)) - return exists_project - except gitlab.GitlabGetError: - return None - - def create_user_project(self, user, project_name): - user.projects.create(data={'name': project_name}) - # делаем еще запрос, для получения проекта со всеми аттрибутами - new_project = self.gl.projects.get('{}/{}'.format(user.username, project_name)) - return new_project - - def get_or_create_user_project(self, user, project_name): - project = self.get_user_project(user, project_name) - if project is None: - project = self.create_user_project(user, project_name) - return project - - def make_user_project_master(self, user, project): - try: - project.members.create(data=dict( - user_id=user.id, - access_level=40, - )) - except gitlab.GitlabCreateError: - pass - - def copy_files_to_repository(self, base_project, files_path, target_project, autor): - commit_actions = [] - items = base_project.repository_tree(path=files_path, recursive=True) - for item in items: - # item = {'name': '__init__.py', 'path': 'module_01/lesson_01/__init__.py', 'type': 'blob', - # 'id': '633f866158ac742cf754a2c43edcb07e3a094f3c', 'mode': '100644'} - if item['type'] == 'blob': - file_sha = item['id'] - file_info = base_project.repository_blob(sha=file_sha) - # file_info = {'content': 'IyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KCg==', 'size': 25, - # 'sha': '633f866158ac742cf754a2c43edcb07e3a094f3c', 'encoding': 'base64'} - action = dict( - action='create', - file_path=item['path'], - content=file_info['content'], - encoding='base64', - ) - commit_actions.append(action) - commit_data = { - 'branch': 'master', - 'commit_message': 'Add {}'.format(files_path), - 'actions': commit_actions - } - try: - commit = target_project.commits.create(commit_data, sudo=autor) - return commit - except gitlab.GitlabCreateError as exc: - if exc.response_code == 400: - raise GitlabWrapperFilesExists() - raise diff --git a/gitlab_service/migrations/__init__.py b/gitlab_service/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gitlab_service/models.py b/gitlab_service/models.py deleted file mode 100644 index dd796bb..0000000 --- a/gitlab_service/models.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.db import models -from model_utils import Choices - - -class User(models.Model): - ROLES = Choices( - ('student', 'Студент'), - ('teacher', 'Преподователь'), - ) - - token = models.CharField(max_length=256) - role = models.CharField(max_length=32, choices=ROLES, default=ROLES.student) - - -class Repository(models.Model): - user = models.ForeignKey(User) - name = models.CharField(max_length=256) - - diff --git a/gitlab_service/tests.py b/gitlab_service/tests.py deleted file mode 100644 index 3c38043..0000000 --- a/gitlab_service/tests.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -from django.test import TestCase - -# Create your tests here. diff --git a/gitlab_service/views.py b/gitlab_service/views.py deleted file mode 100644 index 050a3d8..0000000 --- a/gitlab_service/views.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.shortcuts import render - -# Create your views here. diff --git a/lms/settings.py b/lms/settings.py index f55139e..4e605b6 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -128,6 +128,7 @@ MIDDLEWARE_CLASSES = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', #'access.middleware.CheckPerm', 'access.middleware.RequestToApi', + 'access.middleware.UpdateActivity', ] ROOT_URLCONF = 'lms.urls' diff --git a/storage/api.py b/storage/api.py index 8b8e586..5a82bab 100644 --- a/storage/api.py +++ b/storage/api.py @@ -1,7 +1,7 @@ from storage.models import Comment, File -def upload_file(original=None, name=None, base64=None) -> File: +def upload_file(original=None, name=None, base64=None, **_kwargs) -> File: if original: new_file = File.objects.create(original=original) else: @@ -30,6 +30,7 @@ def add_comment(text: str, out_key: str, files=None) -> Comment: for file in files: new_file = upload_file(**file) + print(new_file) comment.files.add(new_file) return comment diff --git a/storage/models.py b/storage/models.py index f2b12b3..bdc363d 100755 --- a/storage/models.py +++ b/storage/models.py @@ -1,7 +1,5 @@ # encoding=utf-8 import base64 -import random -import string import uuid from django.core.files.base import ContentFile @@ -11,11 +9,10 @@ from django.db import models class FileManager(models.Manager): def upload_as_base64(self, file_base64): - key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) if "data:" in file_base64: my_str = file_base64[file_base64.index("base64,") + 7:] ext = my_str.split('/')[-1] - file = self.create(key, original=ContentFile(base64.b64decode(my_str), name='time.' + ext)) + file = self.create(original=ContentFile(base64.b64decode(my_str), name='time.' + ext)) return file raise ValueError() diff --git a/storage/views.py b/storage/views.py index 2d903aa..95b11e4 100644 --- a/storage/views.py +++ b/storage/views.py @@ -2,6 +2,7 @@ from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView +from storage.api import add_comment from storage.models import File, Comment from storage.serializers import FileSerializer, CommentSerializer @@ -29,6 +30,20 @@ class FileView(APIView): class CommentView(APIView): renderer_classes = (JSONRenderer,) + @staticmethod + def post(request): + text = request.JSON.get('text', None) + user_key = request.JSON.get('user_token', None) + files = request.JSON.get('files', []) + + if user_key: + print(user_key) + comment = add_comment(text=text, out_key=user_key, files=files) + print(comment) + return Response(CommentSerializer(comment).data, status=200) + + return Response("user_token mast be set", status=400) + @staticmethod def get(request): tokens = request.GET.getlist('tokens', None)