user out_key

feature/fix_generate_pass
Andrey 8 years ago
parent f7ea328315
commit 7f9f579d53
  1. 6
      access/admin.py
  2. 19
      access/middleware.py
  3. 31
      access/migrations/0003_useractivity.py
  4. 16
      access/models/other.py
  5. 3
      access/models/user.py
  6. 16
      access/serializers.py
  7. 4
      access/views.py
  8. 2
      courses/serializers.py
  9. 0
      gitlab_service/__init__.py
  10. 5
      gitlab_service/admin.py
  11. 88
      gitlab_service/api.py
  12. 7
      gitlab_service/apps.py
  13. 106
      gitlab_service/gitlab_wrapper.py
  14. 0
      gitlab_service/migrations/__init__.py
  15. 21
      gitlab_service/models.py
  16. 4
      gitlab_service/tests.py
  17. 5
      gitlab_service/views.py
  18. 1
      lms/settings.py
  19. 3
      storage/api.py
  20. 5
      storage/models.py
  21. 15
      storage/views.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)
@ -10,3 +9,4 @@ admin.site.register(Progress)
admin.site.register(Invite)
admin.site.register(ResetPassword)
admin.site.register(ProgressLesson)
admin.site.register(UserActivity)

@ -1,6 +1,7 @@
from django.http import HttpResponseForbidden
from django.http import QueryDict
from access.models.other import UserActivity
import json
@ -17,7 +18,6 @@ class CheckPerm(object):
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()
@ -34,3 +34,20 @@ class RequestToApi(object):
if request.method == 'POST' or request.method == 'DELETE':
# TODO или выпилить или в зависимость от settings
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()

@ -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': 'Пользовательская активность',
},
),
]

@ -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)

@ -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:

@ -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
@staticmethod
def get_last_request(self):
return self.useractivity.last_request

@ -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)

@ -26,7 +26,7 @@ class LessonSerializer(MiniLessonSerializer):
class Meta:
model = Lesson
exclude = ('id', 'topic', 'free')
exclude = ('id', 'topic', 'free', 'key')
class CourseInitSerializer(serializers.ModelSerializer):

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
# Register your models here.

@ -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

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class GitlabServiceConfig(AppConfig):
name = 'gitlab_service'

@ -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

@ -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)

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
# Create your tests here.

@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
from django.shortcuts import render
# Create your views here.

@ -128,6 +128,7 @@ MIDDLEWARE_CLASSES = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
#'access.middleware.CheckPerm',
'access.middleware.RequestToApi',
'access.middleware.UpdateActivity',
]
ROOT_URLCONF = 'lms.urls'

@ -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

@ -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()

@ -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)

Loading…
Cancel
Save