Не взяли на работу из-за неправильно выполненного тестового задания, посмотрите?

Привет.
Устраиваюсь в одну контору на Junior Python 40 тыс.р до вычета. Сегодня ответили: “Бла бла блa"На данный момент не готовы сотрудничать по этой вакансии: есть замечания к тестовому”.
Выполнил тестовое за полтора дня - итого часов 8, учитывая, что ранее вообще на питоне не писал. Они проверяли 4 дня, у меня никогда так долго еще не проверяли тестовые, там по сути и проверять нечего.
Прилагаю ТЗ и написанный скрипт + пояснения. Там плевое дело.
Опытные товарищи, посмотрите пожалуйста, где же я наделал “замечаний”. Может действительно это никуда не потянет, или не взяли по другим причинам.
ТЗ:
5e722634be2a6655041740.png
Код:
# -*- coding: utf-8 -*-
import os
import requests
import json
import time

TASKS_DIR = os.path.dirname(__file__) + '/tasks'
USERS_GET_LINK = 'https://jsonplaceholder.typicode.com/users'
TASKS_GET_LINK = 'https://jsonplaceholder.typicode.com/todos'


def error(text, is_exit=True):
    print(text)
    if is_exit:
        exit(0)

if not os.path.isdir(TASKS_DIR):
    try:
        os.mkdir(TASKS_DIR)
    except Exception:
        error(u'Не удалось создать боевую директорию')

try:
    users_data = requests.get(USERS_GET_LINK)
except Exception:
    error(u'Не удалось получить пользователей | get_http_error')
users_data = users_data.text
try:
    users_data_json = json.loads(users_data)
except Exception:
    error(u'Не удалось получить пользователей | json_error')
try:
    tasks_data = requests.get(TASKS_GET_LINK)
except Exception:
    error(u'Не удалось получить задачи | get_http_error')
tasks_data = tasks_data.text
try:
    tasks_data_json = json.loads(tasks_data)
except Exception:
    error(u'Не удалось получить задачи | json_error')

files_arr = []
for user in users_data_json:
    file_data = user['name'] + '<' + user['email'] + '> ' +\
        time.strftime('%d.%m.%Y %H:%M', time.localtime()) + '\n' +\
        user['company']['name'] + '\n\n'
    compl_tasks = ''
    uncompl_tasks = ''
    for task in tasks_data_json:
        if task['userId'] == user['id']:
            if len(task['title']) > 50:
                task_title = task['title'][:50] + '...'
            else:
                task_title = task['title']
            if task['completed']:
                compl_tasks += task_title + '\n'
            else:
                uncompl_tasks += task_title + '\n'
    file_data += u'Завершённые задачи:\n' + compl_tasks + '\n'
    file_data += u'Оставшиеся задачи:\n' + uncompl_tasks
    files_arr.append({'username': user['username'], 'data': file_data})

old_files_c = 0
new_files_c = 0
for file in files_arr:
    username = file['username']
    data = file['data']
    file_path = TASKS_DIR + '/' + username + '.txt'
    if os.path.isfile(file_path):
        new_path = TASKS_DIR + '/' + username + '_' +\
            time.strftime('%Y-%m-%dT%H:%M',
                          time.localtime(os.path.getctime(file_path))) + '.txt'
        try:
            os.rename(file_path, new_path)
            old_files_c += 1
        except Exception:
            error(u'Не удалось переименовать старый файл ' + file_path +
                  ' в ' + new_path + '\n', False)
    try:
        with open(file_path, 'w') as f:
            f.write(data.encode('utf-8'))
        new_files_c += 1
    except Exception:
        error(u'Не удалось создать актуальный файл ' + file_path + '\n', False)

print(u'Успешно создано новых файлов ' + str(new_files_c) +
      u'\nПереименованно старых: ' + str(old_files_c))


Пояснения:

Скрипт для получения задач пользователей

Позволяет получить задачи по пользователям и сохранить инфу в файлы. Похож на генерацию файлов sitemap.xml для сайтов. Подразумевается использование с периодическим запуском на фоне как демона.
Требования и использование

Для работы скрипта необходим интерпретатор Python 3

Запуск python /path/to/script/get_users_tasks.py
Вариант 2 - актуализация всех файлов пачкой

Если требуется чтобы файлы актуализировались все пачкой или никак. Если юзеров и задач будет огромное кол-во, лучше не хранить в памяти все данные для создания файлов, а брать по несколько и записывать на диск, дабы не поймать out of memory.
Возможный алгоритм

Копируем боевую директорию DIR1 в DIR2. Генерируем файлы по актуальным данным в DIR2. По завершении создаем симлинк TASKS на DIR2 ln -s /path/dir2 /path/tasks. Теперь внешняя программа (человек) будет тянуть свежие файлы из DIR2. После первой итерации скрипта как демона предусматриваем смену симлинка, также после генерации rm /path/tasks.

Преимущества:

изолированность версий, на бою будут все актуальные файлы одной версии.

Недостатки:

возможен простой внешней программы, которая использует файлы, если сервер упадет между выполнением команд rm /path/tasks ...server down... ln -s /path/dir{n} /path/tasks. Нивелируется автоматической проверкой и исправлением после подъема сервера.
требует больше места на харде, т.к. полностью копируется текущая актуальная директория.

Вариант 3 - используем СУБД

Храним данные в BLOB полях, весь процесс строим на транзакциях. Можно организовать динамическую генерацию файлов по запросу скриптом-прокладкой. Например GoogleBot пришел за /path/sitemap_1.xml, /path/sitemap_2.xml, а статичных файлов нет, там запросы обрабатывает дежурный скрипт, который генерирует сайтмап на лету и отдает.

Преимущества:

лучший вариант для данного рода задач.

Недостатки:

придется переделать прием входных данных у внешней программы с файлов на БД, в том числе скриптом обрабочиком.
  • Вопрос задан
  • 1724 просмотра
Пригласить эксперта
Ответы на вопрос 9
dimonchik2013
@dimonchik2013
армяне в ночных клубах теперь не "итальянцы"
Братан, индусский код, без обид

1)
TASKS_DIR = os.path.dirname(__file__) + '/tasks'
тут os.path.join нужен ( + - еще можно поспорить, если б см. п 2 ты правильно заюзал)

2)
file_data = user['name'] + '<' + user['email'] + '> ' +\
        time.strftime('%d.%m.%Y %H:%M', time.localtime()) + '\n' +\
        user['company']['name'] + '\n\n'


это пишется так
file_data = f"{user['name']} < {user['email']} + >  {time.strftime('%d.%m.%Y %H:%M', time.localtime())} \n {user['company']['name']} \n\n"

и вот так еще полубезобидно можно складывать путь из п1.

3)
except Exception as e:
     print(e, u'Не удалось получить задачи | get_http_error')

есть такая вещь как ООП и паттерны, и там наружу тебе нужно передавать естестенную (читай систмную, сетевую, АПИ) ошибку, а не самодеятельность - ну нет прав на запись в твою создаваемую ДИР, что делать запустившему - код твой гадать? так он сразу увидит, что нет прав, апи кей не катит, нет роута и т.д.

ну и , как понимаешь, весь блок коннекта к апи можно было обернуть одиним - какая разница где ошибка? ты ее выведешь, при общем неуспехе

но ты стараешьтся, молодец - нет camel style по крайней мере (надеюсь, это осознанно? )
Ответ написан
@RuComMarket
Битрикс FullStack разработчик
что касается замечаний не скажу, но если они есть, то их должны объяснить.
Тут скорее дело в другом, и причин может быть много.
сейчас программистов способных что-либо путное писать единицы.
Если задание выполнено и работает, если даже есть замечания, дальше ведут беседу выявляя, почему ты допустил "замечания" и вообще понял ли ты о них.
Очень часто встречается когда среди отбирающих присутствует программист, с которого начинались разработки компании, и который сам не умеет программировать, но с учетом что он когда-то написал для компании "Hello World" ориентир руководства на его мнение. Он может по разным причинам отклонить:
- может он не знает язык на столько, чтобы читать чужой код
- может он не хочет что бы кто-то лучше него разбирающийся работал
- может он вообще не хочет чтобы кто-то еще работал, но руководство дало задачу искать
я таких моментов за свою практику встречал примерно в половине компаний с которыми сталкивался как в поиске работы, так и сотрудничая по другим задачам.

Не стоит обращать внимания на такие компании и, главное, не опускать руки.
за 40к питонщика возьмут любого, даже с "замечаниями", т.к. хороший питонщик на такие копейки просто даже не откликнется.
ищи работу дальше, чтобы нарабатывать опыт. Если задаешь такие вопросы, думаю с таким подходом через годик другой увеличишь зарплатные запросы раза так в 3)
Ответ написан
@bacon
Без вникания в реализацию, общее
1. Просят Python 3, а мы видим строки из второго
2. Объединение строк на плюсах, так же подобно сделано формирование путей
3. Общий except
Ответ написан
про PEP 8 вы не прочитали.
https://pythonworld.ru/osnovy/pep-8-rukovodstvo-po...

про except Exception вообще молчу - это "костыль", а нужна вменяемая обработка ошибок.
Ответ написан
saboteur_kiev
@saboteur_kiev Куратор тега Карьера
build engineer
Без придирок к стилистике, вы не выполнили собственно самое главное - в ТЗ несколько раз упоминается требование к надежности.
А у вас - переименовали старый отчет, затем пишете новый отчет.

А если сбойн? Старый уже переименован, новый еще не дописан? Где выполнение требования отказоустойчивости?
Ответ написан
b0nn1e
@b0nn1e
Alcohol & Ruby on Rails
Какая вообще разница, из-за тестового или нет.
Не взяли - значит не подходите.
Представьте что на той стороне сидит чувак, которому единоразово пришло штук 50 одинаковых решений одного задания и ему нужно отобрать в лучшем случае 10, тех с кем стоит дальше общаться.

Указание ожидаемой зп вообще не уместно, обычно компании вообще не имеет значения 10к вам платить или 100к. Сравнение идет с такими-же товарищами как вы.

Представьте что кто-то где-то сделал задание чуть лучше или на таком-же уровне но попросил 30к.
Ответ написан
Jump
@Jump
Системный администратор со стажем.
Не взяли на работу из-за неправильно выполненного тестового задания,
С чего вы так решили?

есть замечания к тестовому
Замечания можно с легкостью найти даже к идеальному коду.
А у вас код далек от идеала, поэтому поле для замечаний очень широкое.

А не взяли возможно потому что вы им не понравились.
Ответ написан
artygrand
@artygrand
Прогер, кодер, писатель кода
Помимо прочих замечаний я бы добавил, что
for task in tasks_data_json:
        if task['userId'] == user['id']:

заставит код бегать по массиву 10 раз в твоем случае. А вообще это n_юзеров * m_тасков вызовов IF.
Можно сильно сократить, если один раз пройтись по массиву и добавить эти данные в словарь, вида
{user_id: [
   [task_done, task_done],
   [task_no, task_no]
],
}

И уже 10 раз обратиться по ключу.
Ответ написан
@500rur
У вас написано в требованиях к заданию
1. Код должен быть чистым
А тут мешанина из огромного количества try/except, причем никакой обработки исключений нет. Думаю, что всех их можно выкинуть.
2. Код должен быть разбит на функции.
В примере только одна функция, она не выполняет никакой полезной работы .
3. Код должен быть эффективный, но читабельный.
Если код не читаем, то до этого пункта никто даже и не доберется.

Кмк основная проблема в том, что автор пересолил с обработкой исключений :) Ну и код должен быть таким, что с первого взгляда видно, что он делает (должна быть разбивка на функции). Да, еще поскольку был помянут linux, то я бы добавил shebang в начало файла.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы