Skip to content

Commit

Permalink
Merge pull request #43 from moevm/bulk_import
Browse files Browse the repository at this point in the history
User impex added
  • Loading branch information
Saprigenie authored Dec 15, 2023
2 parents b6fb740 + 97c3a7b commit a7c36c9
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 23 deletions.
30 changes: 21 additions & 9 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,27 @@
</router-link>
</li>

<li v-if="userStore.role === UserRole.admin" class="nav-item ps-4">
<router-link
:to="{ name: routeNames.Users }"
class="nav-link text-warning"
active-class="text-info"
>
Пользователи
</router-link>
</li>
<template v-if="userStore.role === UserRole.admin">
<li class="nav-item ps-4">
<router-link
:to="{ name: routeNames.Users }"
class="nav-link text-warning"
active-class="fw-bold"
>
Пользователи
</router-link>
</li>

<li class="nav-item ps-4">
<router-link
:to="{ name: routeNames.Dumps }"
class="nav-link text-danger"
active-class="fw-bold"
>
Дампы
</router-link>
</li>
</template>
</ul>
</div>
<div class="fs-2 text-primary" role="button">
Expand Down
98 changes: 98 additions & 0 deletions client/src/components/routes/dumps/DumpComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<template>
<div class="container mt-3">
<ul id="myTab" class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<button
id="home-tab"
class="nav-link active"
data-bs-toggle="tab"
data-bs-target="#maps"
>
Экспорт
</button>
</li>
<li class="nav-item" role="presentation">
<button
id="profile-tab"
class="nav-link"
data-bs-toggle="tab"
data-bs-target="#import"
>
Импорт
</button>
</li>
</ul>
<div class="tab-content mt-3">
<div id="maps" class="tab-pane fade show active">
<div class="m-auto col-10 col-lg-6 text-center">
<button class="btn btn-primary fs-3" @click="download">
Загрузить
<i class="bi bi-file-earmark-arrow-down fw-bold" />
</button>
</div>
</div>
<div id="import" class="tab-pane fade">
<div class="m-auto col-10 col-lg-6">
<FormKit type="form" :actions="false" @submit="upload">
<FormKit
name="files"
type="file"
accept=".json"
multiple="false"
label="Дамп"
validation="required"
validation-visibility="live"
/>
<FormKit
outer-class="text-end"
input-class="$reset btn btn-success"
type="submit"
label="Загрузить"
/>
</FormKit>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { FormKitGroupValue } from "@formkit/core";
import { useToaster } from "@/store/toaster";
import { ToastTypes } from "@/config/toast";
import { ref } from "vue";
import { saveAs } from "file-saver";
import { DumpsApi } from "@/components/routes/dumps/api";
const toaster = useToaster();
const files = ref<{ name: string; file: File }[]>([]);
async function download() {
const data = (await DumpsApi.downloadDump()).data;
const blob = new Blob([JSON.stringify(data)], {
type: "text/plain;charset=utf-8",
});
saveAs(blob, "dump.json");
}
async function upload(data: FormKitGroupValue) {
try {
await DumpsApi.uploadDump((data.files as { file: File }[])[0].file);
toaster.addToast({
title: "Информация",
body: "Дамп загружен успешно",
type: ToastTypes.success,
});
} catch (e) {
toaster.addToast({
title: "Информация",
body: "Не удалось загрузить Дамп",
type: ToastTypes.danger,
});
}
}
</script>

<style scoped lang="scss"></style>
14 changes: 14 additions & 0 deletions client/src/components/routes/dumps/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { api } from "@/api";
import { Dump } from "@/components/routes/dumps/types";

export function downloadDump() {
return api.get<Dump>("/dumps/");
}

export function uploadDump(file: File) {
const formData = new FormData();
formData.append("dump", file);
return api.post("/dumps/", formData);
}

export const DumpsApi = { downloadDump, uploadDump };
7 changes: 7 additions & 0 deletions client/src/components/routes/dumps/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ObjectInfo } from "@/types/objects";
import { User } from "@/types/users";

export interface Dump {
objects: ObjectInfo[];
users: User[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="container-lg mt-3">
<h3>База объектов</h3>
<div class="text-end mb-2" @click="exportData">
<button class="btn btn-primary">Экспорт</button>
<button class="btn btn-primary">Экспорт объектов</button>
</div>
<AgGridVue
class="ag-theme-alpine"
Expand Down
9 changes: 4 additions & 5 deletions client/src/components/routes/upload/UploadComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
data-bs-toggle="tab"
data-bs-target="#import"
>
Импорт
Импорт объектов
</button>
</li>
</ul>
<div id="myTabContent" class="tab-content">
<div id="maps" class="tab-pane fade show active mt-3">
<div class="tab-content mt-3">
<div id="maps" class="tab-pane fade show active">
<div class="m-auto col-10 col-lg-6">
<FormKit type="form" :actions="false" @submit="submit">
<FormKit
Expand All @@ -50,7 +50,7 @@
</FormKit>
</div>
</div>
<div id="import" class="tab-pane fade mt-3">
<div id="import" class="tab-pane fade">
<div class="m-auto col-10 col-lg-6">
<FormKit type="form" :actions="false" @submit="submitImport">
<FormKit
Expand Down Expand Up @@ -125,7 +125,6 @@ async function submit(data: FormKitGroupValue) {
}
async function submitImport(data: FormKitGroupValue) {
console.log(data);
try {
await uploadObjects((data.files as { file: File }[])[0].file);
Expand Down
8 changes: 8 additions & 0 deletions client/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const routeNames = {
Auth: "Auth",
Users: "Users",
Profile: "Profile",
Dumps: "Dumps",
};

export const routePaths = {
Expand All @@ -24,6 +25,7 @@ export const routePaths = {
[routeNames.Auth]: "/auth",
[routeNames.Users]: "/users",
[routeNames.Profile]: "/profile",
[routeNames.Dumps]: "/dumps",
};

export const routes: RouteRecordRaw[] = [
Expand Down Expand Up @@ -87,6 +89,12 @@ export const routes: RouteRecordRaw[] = [
path: routePaths[routeNames.Profile],
component: () => import("@/views/ProfileView.vue"),
},

{
name: routeNames.Dumps,
path: routePaths[routeNames.Dumps],
component: () => import("@/views/DumpView.vue"),
},
];

export const router = createRouter({
Expand Down
7 changes: 7 additions & 0 deletions client/src/views/DumpView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<DumpComponent />
</template>

<script setup lang="ts">
import DumpComponent from "@/components/routes/dumps/DumpComponent.vue";
</script>
2 changes: 2 additions & 0 deletions server/app/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .auth import api as auth_api
from .objects import api as objects_api
from .tiles import api as tiles_api
from .dump import api as dump_api

api = Api(
title="Ecology API",
Expand All @@ -18,3 +19,4 @@
api.add_namespace(images_api)
api.add_namespace(tiles_api)
api.add_namespace(objects_api)
api.add_namespace(dump_api)
30 changes: 30 additions & 0 deletions server/app/routes/dump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from flask import request
from flask_login import login_required
from flask_restx import Namespace, Resource
from werkzeug.local import LocalProxy
import json

from app.db import get_db
from app.services.objects import bulk_upload_objects
from app.services.user import bulk_upload_users
from app.utils import parse_json

db = LocalProxy(get_db)

api = Namespace("dumps", description="Дампы")


@api.route('/')
class DumpResource(Resource):
def get(self):
return parse_json({
'objects': db.objects.find({}),
'users': db.users.find({})
})

@login_required
def post(self):
dump = json.load(request.files['dump'])
bulk_upload_objects(dump['objects'])
bulk_upload_users(dump['users'])
return "Ok"
9 changes: 2 additions & 7 deletions server/app/routes/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json

from app.db import get_db, get_tiles, get_maps, get_redis
from app.services.objects import bulk_upload_objects
from app.utils import parse_json

db = LocalProxy(get_db)
Expand Down Expand Up @@ -136,11 +137,5 @@ def get(self):
@login_required
def post(self):
new_objects = json.load(request.files['objects'])
object_keys = ["_id", "type", "name", "color", "update", "coordinates", "center"]
for obj in new_objects:
for k in object_keys:
if k not in obj:
return "Not ok"
del obj['_id']
db.objects.insert_many(new_objects)
bulk_upload_objects(new_objects)
return "Ok"
17 changes: 16 additions & 1 deletion server/app/routes/user.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import json

from flask import request
from flask_login import login_required, current_user
from flask_restx import Namespace, Resource, fields
from werkzeug.local import LocalProxy

from app.auth.authorization import role_require
from app.db import get_db
from app.services.user import get_user_by_id, find_user, update_user, delete_user
from app.services.user import get_user_by_id, find_user, update_user, delete_user, bulk_upload_users
from app.utils import parse_json

api = Namespace("users", description="Операции с пользователями")
Expand Down Expand Up @@ -104,3 +107,15 @@ def put(self):
args = dict((k, v) for k, v in args.items() if v is not None)
update_user(current_user.get_id(), args)
return 'Updated'


@api.route('/impex')
class UserResource(Resource):
def get(self):
return parse_json(db.users.find({}))

@login_required
def post(self):
new_users = json.load(request.files['users'])
bulk_upload_users(new_users)
return "OK"
17 changes: 17 additions & 0 deletions server/app/services/objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from werkzeug.local import LocalProxy

from app.db import get_db

db = LocalProxy(get_db)


def bulk_upload_objects(new_objects):
object_keys = ["_id", "type", "name", "color", "update", "coordinates", "center"]
for obj in new_objects:
for k in object_keys:
if k not in obj:
raise Exception('invalid object property')
del obj['_id']

if len(new_objects):
db.objects.insert_many(new_objects)
18 changes: 18 additions & 0 deletions server/app/services/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,21 @@ def update_user(id: str, data):

def delete_user(id: str):
db.users.delete_one({"_id": ObjectId(id)})


def bulk_upload_users(new_users):
users_keys = ["_id", "login", "password", "name", "role"]
for user in new_users:
for k in users_keys:
if k not in user:
raise Exception('invalid user property')
del user['_id']

# Пропускаем уже существующих пользователей
new_users = list(filter(
lambda user: not db.users.find_one({"login": user["login"]}),
new_users
))

if len(new_users):
db.users.insert_many(new_users)

0 comments on commit a7c36c9

Please sign in to comment.