Русский
Русский
English
Статистика
Реклама

Office

АнтиBIMing

22.05.2021 20:11:20 | Автор: admin
image
Сама по себе автоматизация лишь инструмент и как каждый инструмент у нее есть своя область применения, своя техника безопасности внедрения и применения, а так же свои преимущества и негатив. Традиционно бизнес стремится внедряться IT-разработки там, где существуют достаточно высокая маржа, а значит проще получить прибыль и уменьшать издержки, однако существуют области в которых давно назрела необходимость что-нибудь внедрить с тем что бы упростить и тогда все сформируется. Речь о личном опыте решения таких задач при составлении исполнительной документации в строительстве.

Программа, в которой описываются основные понятия и определения встречающиеся в тексте
Состав ПСД. Приемо-сдаточная документация#1 делится на:
1. Разрешительная документация, включая ППР;
2. Исполнительная документация.

Вся структура приемо-сдаточной документации субподрядной организации по спецмонтажным работам будет выглядеть так:
Разрешительная документация#2 термин, используемый для обозначения документации, оформляемой в соответствии со статьями 45 51 Градостроительного кодекса РФ вплоть до получения разрешения на строительство (ст. 51 ГрКРФ), а также получение разрешения на ввод объекта в эксплуатацию (ст. 55 ГрКРФ).
Исполнительная документация (ИД)#2 представляет собой текстовые и графические материалы, отражающие фактическое исполнение проектных решений и фактическое положение объектов капитального строительства и их элементов в процессе строительства, реконструкции, капитального ремонта объектов капитального строительства по мере завершения определенных в проектной документации работ.

Исполнительная документация, оформленная соответствующим образом, является документом построенного здания или сооружения, облегчающим процесс эксплуатации, отражающим техническое состояние, дающим четкое представление об ответственных производителях работ по любому из видов выполненных работ.

ОЖР Общий Журнал Работ. является основным документом подтверждающим выполнение работ, отражает последовательность выполнения отдельных видов работ, в том числе сроки и условия выполнения работ при строительстве, реконструкции, капитальном ремонте, а также сведения о строительном контроле и государственном строительном надзоре.

АОСР Акт освидетельствования скрытых работ. Некоторые работы после окончания строительства невозможно проверить. Это касается работ, проведенных внутри строительных конструкций и коммуникаций, например:
установки арматуры в бетонных стенах,
грунтования поверхности перед окрашиванием,
толщины песочной подложки под брусчаткой,
укладки труб перед засыпанием грунтом,
гидро, звуко-, теплоизоляции и т.п.

Такие промежуточные работы называются скрытыми и их нельзя увидеть глазами после завершения строительства, поэтому для подтверждения их качества составляются специальные акты непосредственно после их выполнения. Механизм данной проверки такой: до закрытия конструкций или коммуникаций, специально созданная комиссия проверяет качество выполненных работ и примененных материалов, а затем подписывает акт на скрытые работы.

Действие Первое. В котором рассказывается про предпосылки для автоматизации и проблемные рутины в Строительстве

Ты что делаешь?
Анекдоты читаю.
А отчет?
Час назад уже у тебя на столе лежит.
Погоди, тогда почему твой предшественник на его подготовку тратил три часа?
Послушай, я тоже могу тратить три часа на его подготовку. Если хочешь, я могу читать анекдоты в столовой. Но результат будет тот же.
Структура договорных отношений между участниками строительства #1
Обычно Инвестор нанимает организацию, занимающуюся управлением строительства, она может называться заказчиком. Этот заказчик нанимает проектный институт (лицо, осуществляющее подготовку проектной документации), чтобы тот ему нарисовал проект, бывает, так же нанимает генпроектировщика, а тот нанимает субчиков. Потом играют в тендер (кстати, то же самое может быть и с институтом) и выбирают генподрядчика это ответственный за строительную площадку (лицо, осуществляющее строительство) и заключает с ним договор. Для заказчика существует только генподрядчик (подрядчик) так как им так легче и удобней работать. Генподрядчик уже без тендера выбирает себе субподрядчиков (лицо, выполняющее работы), обычно по видам работ и заключает с ними договора. Субподрядчик или даже сам генподрядчик так же часто себе набирает субчиков, но уже не официально как бы под своим флагом. Заказчик нанимает технический надзор или сам может выполнять данную функцию (представитель заказчика или технический надзор заказчика). Если объект подпадает под государственный строительный надзор (ГСН), то и следит за всем этим он в виде инспекторов, их уведомляет заказчик о начале строительства, те приезжают со своей инспекцией, пишут замечания и уезжают. Все отношения регулируются договорами и действующим законодательством.

Строительство ведется по рабочим чертежам (заверенных печатью В производство работ) теми материалами и оборудованием, что договорились, их может поставлять как заказчик с генподрядчиком, так и субподрядчик. Строительство ведется в определенные сроки по графику. Каждый месяц Заказчик платит Подярдчику, а Позрядчик Субподрядчику и т.д. по цепочке, за построенные объемы работ денежку, из которой в т.ч. платится зарплата, а представители Подрядчика/Судподрядчика отчитываются за материалы и оборудование, которые были переданы (все это обговорено в договоре). Проверяет объемы и качество выполненных работ технический надзор, авторский надзор так же может следить за соответствием выполняемых работ по проекту и рассматривает возможность изменений в проекте (если с институтом был подписан на это договор и на опасном производственном объекте он должен быть по любому).

Каждый день нормальные генподрядчики или заказчики вместе со всеми участниками строительства проводят планерки, где проверяют выполнение графика, планируют его на ближайшие дни и решают возникающие проблемы.

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

При производстве работ по строительству объекта ведется всевозможная документация, основной и самой сложной частью из которой является исполнительная.


Согласно Википедии
BIM (англ. Building Information Model или Modeling) информационная модель (или моделирование) зданий и сооружений, под которыми в широком смысле понимают любые объекты инфраструктуры, например инженерные сети (водные, газовые, электрические, канализационные, коммуникационные), дороги, железные дороги, мосты, порты и тоннели и т. д.

Информационное моделирование здания это подход к возведению, оснащению, эксплуатации и ремонту (а также сносу) здания (к управлению жизненным циклом объекта), который предполагает сбор и комплексную обработку в процессе проектирования всей архитектурно-конструкторской, технологической, экономической и иной информации о здании со всеми её взаимосвязями и зависимостями, когда здание и всё, что имеет к нему отношение, рассматриваются как единый объект.

Трёхмерная модель здания, либо другого строительного объекта, связанная с базой данных, в которой каждому элементу модели можно присвоить все необходимые атрибуты. Особенность такого подхода заключается в том, что строительный объект проектируется фактически как единое целое: изменение какого-либо из его параметров влечёт за собой автоматическое изменение связанных с ним параметров и объектов, вплоть до чертежей, визуализаций, спецификаций и календарного графика.

Что касается документации и информационной модели на стройке и откуда она там берется. Как правило Заказчик передает Подрядчику проект со штампом В производство работ на бумажном носителе (очень редко в формате pdf и почти никогда в dwg) для того что бы последний в соответствии с контрактом за оговоренную сумму произвел некоторые работы. Прораб/мастер/нач.участка заказывает через снабженцев (привет бухгалтерия и 1С) материалы согласно потребностям, проекту и графику производства работ, нанимается техника, она же ремонтируется, под нее покупается ГСМ и прочие расходы связанные с объектом, часть из которых связана с машинами и механизмами, часть с материалами которые будут монтироваться. Затем на объекте ведутся на бумажном носителе: журнал входного контроля, общий журнал работ и прочие специализированные журналы которые зависят от видов выполняемых работ. К концу каждого операционного цикла подготавливается исполнительная документация, которая представляет из себя акты и схемы выполненных работ (схемы по сути копируют проект, ибо отступление от проекта, без согласования с Заказчиком и контролирующими органами, недопустимо и будет означать лишь проблемы для Подрядчика). Такие акты и схемы на бумажном носителе подписываются представителями Подрядчика, Заказчика, контролирующих органов и организаций и только после того как пройдет успешная защита составляются финансовые акты (обычно по форме КС-2 и КС-3, но это не обязательно, достаточно к договору приложить свой шаблон), на основании которых в особо упоротых случаях бухгалтерия Заказчика может позволить списать материалы бухгалтерии Подрядчика (помимо актов выполненных работ составляются так же акты входного контроля и все вместе это передается Заказчику) в соответствии со сметными расценками.

Сегодня, в отличие от СССР, прораб/мастер/нач.участка не составляют исполнительную документацию. Это не означает что они не заполняют и не составляют бумаг, просто они другие, больше связаны с непосредственной организации управленческих процессов (открытие и закрытие нарядов, журналы инструктажа, выдачи заданий, заявки, письма и т.п.) объем бумаг достаточно большой и это нормальная (в том плане, что распространенная практика) брать в штат сотрудников с высшим (!) инженерным образованием инженеров ПТО, которые будут заниматься всей остальной документацией, а проще говоря исполнять работу технического секретаря. (На самом деле порог вхождения в процессию очень низок, т.к. базового школьного курса Черчения достаточно, что бы читать строительные чертежи и даже перерисовывать схемы, конечно потребуется навык работы с Word/Excel/Paint/AutoCAD/Компас, но это не так сложно как может показаться и потому такая специальность утилизирует людей как с профильным образованием, так и с гуманитарным менеджеров/юристов/учителей и т.д. и т.п.)

Как правило рабочее место, которое может быть и удалено (вагончик в поле), оборудовано МФУ, Wi-Fi точкой, раздающей 3G интернет, ноутбуком. В отсутствие сис.админа все это работает в меру сил и понимания инженера ПТО, который за все это отвечает, который выполняет не только прямые обязанности, но и те от которых не удалось отбрыкаться по разным причинам. Надо ли говорить, что общая техническая грамотность страдает. Обычно на ноутбуки установлен, хорошо если заботливо, Windows, MS Office, редактор для векторной графики, GIMP, программа оптического распознавания текстов. Такой скудный выбор связан с тем, что з/п и оснащение такого инженера находится в составе Накладных Расходов, а не в статьях Общей Заработной Платы, как в случае, например, рабочих, т.е. разные статьи расценок сметы.

Действие Второе. В котором рассказывается про предпосылки для автоматизации и проблемные рутины в Строительстве

Исаак Ньютон:
От флюенции возьму флюксию и обратно.
Лейбниц:
Могу делать то же самое!

Идея создания Программы родилась спонтанно, после 3-х закрытых объектов в 2016году ПАО Транснефть. Помимо сбора и компоновки информации большой блок времени отнимали задачи, связанные с банальным заполнением документов по шаблону, среди которых преобладали Акты входного контроля и Акты освидетельствования скрытых работ. Особенно много времени уходило на проверки в случае описок или различного рода неточностей. Т.к. если они выявлялись, то приходилось заново открывать и проверять такие акты. Иногда, как в ситуации в 2018году, когда Ростехнадзор поменял форму актов скрытых работ, их счет шел на десятки. Но так родилась идея: А что, если я соберу все данные, необходимые для заполнения актов, в таблицу, а уже Программа будет прописывать их в шаблоны за меня?.

Самой простой и пригодной из доступных для этого является MS Office с макросами VBA. Учитывая тот факт, что в 90-е годы я в школе ударно изучал QBasic 4.5 и Borland Pascal 7.0, то выбор платформы оказался более чем очевиден. Пробелы в синтаксисе помог закрыть Гугл. Сама Идея не нова, но в 2016-м году в открытом доступе, так сказать в open source, я нашел только один вариант через Слияние, который тогда, в далеком 2016-м году меня не устроил. И вот я начал разрабатывать свой велосипед:

1. Самое главное и без чего не имело все дальнейшее смысл это без наглядности и удобства в работе. Поэтому для варианта с экспортом в Excel был выбран путь перебора текста в ячейках с целью поиска комбинаций текстовых маркеров, которые априори не встречаются в русскоязычных регламентных формах актов, с последующей авто подстановкой значений из таблицы. (Например, f1, d3, b8 и т.д. и т.п.) Для того что бы не пришлось перебирать всю матрицу я ввел упрощение, при создании шаблона если в первой ячейке за областью печати располагается символ арабской единицы, то только в этом случае макрос ищет текст во всех ячейках этой строки. Позднее я решил вопрос как получить в макрос диапазон ячеек, отправляемых на печать.

В случае с экспортом в World тут все гораздо проще Закладки и ссылки на содержимое закладок, при повторном упоминании содержимого в тексте.

2. В отличие от многих конкурентов с более проработанными приложениями я пошел дорогой структурирования информации (привет BIM) и наглядного представления данных, а потому, не смотря на то что тот же Access, Visual Studio, 1С и т.п. предоставляет большие возможности и функционал все эти программы грешат тем, что в них нельзя протянуть строку или столбец с одинаковыми данными, а переключение между полями ввода требует большей точности при позиционировании (выбор поля через TAB или позиционирование курсора с кликом проигрывает в удобстве перемещению стрелками по ячейкам таблицы, не говоря про то, что копировать ячейками проще, чем из через выделения текста из поля ввода)

Следующий шаг это формирование логики заполнения данных, т.е.:

А) Данные организаций и участников строительства, а также общие характеристики объекта;

Б) Данные для формирования Актов входного контроля, т.е. в первую очередь определяемся не с работами, а с материалами

В) Затем определяемся с месячно-суточным графиком, в котором делаем привязку не только к сметам (т.е. сперва вводим сметные расценки в Заголовки), а уже затем формируем перечень работ из ВР (Ведомости объемов работ по РД (Рабочей документации)), соблюдая очередность работ. Т.е. алгоритм после проставления в графике объемов работ сам определит, в рамках каждого отчетного периода, какие Акты скрытых работ и в какой последовательности под каким номером, от каких дат необходимо будет оформить. Пользователю останется лишь внести сопутствующую информацию, которая является больше рутиной. Кроме того, благодаря связям Пользователь может видеть сколько материалов было списано по предыдущим Актам скрытых работ, что

уменьшает кол-во накладок и ошибок, связанных с проведенными материалами и использованными при строительстве (по сути это техническая ошибка, которой можно и нужно избегать).

И вот таким образом ПК/ноутбук превращаются из печатной машинки в помощника, который берет часть обязанностей на себя. Т.е. то, что часто многими упускается из виду при создании программ.

3. Но ведь регламентных форм актов сильно больше чем указанных мною выше 2-х шт., пусть они и представляют собой свыше 50% всех актов Исполнительной документации. Значит нужно создать, и это было сделано, аналогичную программу, которая могла бы заполнять таким же образом любой Пользовательский шаблон, а затем такие шаблоны можно было бы импортировать в основную программу, с тем что бы можно было увязывать текущие данные с новыми формами. А раз новые формы, то значит нужна и в меру удобная навигация. Все эти задачи были решены.

И вот теперь такая программа распространяется абсолютно безвозмездно, т.е. даром. Не смотря на долгий срок разработки одним человеком с длительными перерывами в нерабочее время с 2017-го года по 2021-й год, когда появились все эти Алтиусы, Адепты и прочий платный софт той же тематики я хочу сказать следующие слова критики:

1. Зачастую архитектурные ограничения методов ограничивают юзабилити пользователей. Потому что разработчик не является исполнителем, который в дальнейшем будет пользоваться этой программой, и не изучают технологические операции конкретной специальности, взамен формируя свои технологические-схемы работ с таким софтом, которые не всегда являются удобнее аналогов.

2. Пользователю, т.е. инженерам-ПТО, нужен такой продукт и помощь, который будет прост, удобен и понятен, а еще по совместительству предложит такую схему работы, ошибок при которой будет меньше, чем без нее и трудоемкость уменьшится.

3. Зачастую люди, которым нужна автоматизация, не могут за нее заплатить, т.к. их оклад не такой уж и большой, а в их опыте даже нет рабочих примеров, когда софт облегчал им рабочую рутины, да еще и уменьшал ИХ КОЛИЧЕСТВО ОШИБОК. В то время как цены на такие программы сравнимы со стоимостью Сметных-комплексов. Но без сметных комплексов очень трудно обойтись, а вот без автоматизации Исполнительной документации элементарно.

4. Информационную модель BIM в ходе формирования исполнительной документации приходится формировать заново, работать дешифратором Рабочей документации на бумажном носителе, переводить ее в язык цифровых сигналов с тем, чтобы выводить результат в виде оформленных актов на бумажном носителе, но с подписью. Прочувствуйте всю иронию.

Действие третье. В котором рассказывается о том как кристаллизовалась программа


На стройке самое важное что? График производства работ и ключевые даты на нем (врезка, подключение, начало работ и окончание работ и некоторые другие). На участке ведется ОЖР, в котором записывается вручную что было выполнено за каждый конкретный рабочий день. Но если взять график (Месячно-суточный график) и заполнять его, то мы получим графическое представление, который и легче воспринимается и, затем, легче автоматизируется, служа исходными данными для актов и аналитики.

Рис.1 Пример Месячно-суточного графика

Теперь думаем дальше, если ввести только один раз и дальше расставить ссылки, на ответственных, описание объекта, контракта и организаций, участников строительства, то это зело упростит Работу. Ввел один раз и далее только развешиваешь ссылки.

Следующий Важный момент в строительстве это Материалы. Они должны соответствовать проекту и при их приемке осуществляется Входной контроль комиссией с оформлением бумаг (Акт входного контроля и Журнал Входного контроля). Это держим в уме. А дальше большая часть ИД в строительстве составляют Акты скрытых работ или полностью, Акт освидетельствования скрытых работ. Работы, их очередность и данные подтягиваем с Месячно-суточного графика, а материалы из перечня актов Входного контроля теперь еще и наглядно мониторить можно какие материалы в каком количестве списываются по актам АОСР.

А что если нам нужно иметь гибкость, что бы Пользователь добавлял свои формы актов, делал их вручную или использовал текущие? И это возможно, но обсудим мы это в

Действие четвертое. В котором речь пойдет о макросах VBA

Далее пойдут спойлеры с кодом, призванные решить те или иные вопросы/проблемы.
Немного ускоряем MS Excel при работе с макросами
'Ускоряем Excel путём отключения всего "тормозящего" Public Sub AccelerateExcel()   'Больше не обновляем страницы после каждого действия  Application.ScreenUpdating = False   'Расчёты переводим в ручной режим  Application.Calculation = xlCalculationManual   'Отключаем события  Application.EnableEvents = False   'Не отображаем границы ячеек  If Workbooks.Count Then      ActiveWorkbook.ActiveSheet.DisplayPageBreaks = False  End If   'Отключаем статусную строку  Application.DisplayStatusBar = False   'Отключаем сообщения Excel  Application.DisplayAlerts = False  End Sub

а теперь возвращаем настройки обратно
'Включаем всё то что выключили процедурой AccelerateExcelPublic Sub disAccelerateExcel()   'Включаем обновление экрана после каждого события  Application.ScreenUpdating = True   'Расчёты формул - снова в автоматическом режиме  Application.Calculation = xlCalculationAutomatic   'Включаем события  Application.EnableEvents = True   'Показываем границы ячеек  If Workbooks.Count Then      ActiveWorkbook.ActiveSheet.DisplayPageBreaks = True  End If   'Возвращаем статусную строку  Application.DisplayStatusBar = True   'Разрешаем сообшения Excel  Application.DisplayAlerts = True End Sub

Заполнение шаблонного файла в формате MS Excel
Здесь очень важна наглядность! Очень много проектов встали на абсолютных ссылках, по-этому я изначально решил сделать все универсально. После долгих мытарств я открыл для себя тот факт, что формулой можно получить диапазон ячеек, который будет выводится на печать.


Рис.2 Пример файла шаблона в формате MS Excel

Здесь в ячейке А1 содержится формула:
=СЦЕПИТЬ(АДРЕС(СТРОКА(Область_печати);СТОЛБЕЦ(Область_печати);1;1);":";АДРЕС(СТРОКА(Область_печати)+ЧСТРОК(Область_печати)-1;СТОЛБЕЦ(Область_печати)+ЧИСЛСТОЛБ(Область_печати)-1;1;1))
Т.е. мы можем получить область печати, обратившись к переменной, фигурирующей в диспетчере имен. Полученные абсолютные границы печати, которые будут автоматически меняться, если нам придется увеличить или уменьшить область печати. Зачем? Здесь следует сделать отступление.


Рис.3 Пример листа с хранящимися данными для автоматического заполнения актов.

Дело в том, что мною был выбран способ-маркеров в тексте, т.е. при составлении шаблона маркеры (a1, b0, c7, d8 и т.д. и т.п.) однозначно характеризуют с одной стороны строку, из которой будут браться данные (порядковый номер элемента массива, который автоматически завязан на номер строки), с другой стороны в русскоязычных шаблонах в строительстве абсурдное сочетание букв латиницы с цифрой не используется. А значит это наглядно. После чего обычный перебор текста решает, НО (!) чем больше ячеек в области печати, тем медленнее будет работать алгоритм. Значит ему надо помочь и подсветить только те строки, в которых априори что-то есть.
Код макроса VBA осуществляющий экспорт в шаблон в формате MS Excel
          With Workbooks.Open(ThisWorkbook.Path + "\Шаблоны аддонов\" + NameShablonPrimer, ReadOnly:=True)               .Sheets(NameShablonPrimerList).Visible = -1               .Sheets(NameShablonPrimerList).Copy after:=wb.Worksheets(Worksheets.Count)                              Let НачальныйНомерСтрокиВФайле = .Sheets(NameShablonPrimerList).Cells(1, 2)       ' Начальный номер строки в файле шаблона               Let НачальныйНомерСтолбцаВФайле = .Sheets(NameShablonPrimerList).Cells(1, 3)      ' Начальный номер столбца в файле шаблона               Let КонечныйНомерСтрокиВФайле = .Sheets(NameShablonPrimerList).Cells(1, 4)        ' Конечный номер строки в файле шаблона               Let КонечныйНомерСтолбцаВФайле = .Sheets(NameShablonPrimerList).Cells(1, 5)       ' Конечный номер столбца в файле шаблона                              .Close True          End With       End If    End If    Do       ИмяФайла = BDList + " "                                                                  ' Префикс имени файла       wb.Worksheets(NameShablonPrimerList).Copy after:=Worksheets(Worksheets.Count)       Set новыйЛист = wb.Worksheets(NameShablonPrimerList + " (2)")              For X = НачальныйНомерСтолбцаВФайле To КонечныйНомерСтолбцаВФайле Step 1                  ' Перебираем столбцы в листе Примера формы-шаблона           For Y = НачальныйНомерСтрокиВФайле To КонечныйНомерСтрокиВФайле Step 1                ' Перебираем строк в листе Примера формы-шаблона               If Sheets(новыйЛист.Name).Cells(Y, КонечныйНомерСтолбцаВФайле + 1) = 1 Then       ' При наличии спец символа проверяем на совпадении строку                  Let k = CStr(Sheets(новыйЛист.Name).Cells(Y, X))                               ' Ищем только если в ячейке что-то есть                  If k <> "" Then                     For i = 1 To Кол_воЭл_овМассиваДанных Step 1                         ContentString = CStr(Worksheets(BDList + " (2)").Cells(i + 1, НомерСтолбца))                         If Len(arrСсылкиДанных(i)) > 1 Then                            If ContentString = "-" Or ContentString = "0" Then ContentString = ""                            Let k = Replace(k, arrСсылкиДанных(i), ContentString)                         End If                     Next i                     новыйЛист.Cells(Y, X) = k                  End If               End If           Next Y       Next X                     For Y = НачальныйНомерСтрокиВФайле To КонечныйНомерСтрокиВФайле Step 1           Sheets(новыйЛист.Name).Cells(Y, КонечныйНомерСтолбцаВФайле + 1) = ""       Next Y            

Заполнение шаблонного файла в формате MS Word
вывода в шаблон формата Word, и здесь на самом деле есть 2 способа вывода текста:

1. Это через функционал закладок,
            Rem -= Открываем файл скопированного шаблона по новому пути и заполняем его=-            Set Wapp = CreateObject("word.Application"): Wapp.Visible = False            Set Wd = Wapp.Documents.Open(ИмяФайла)                        NameOfBookmark = arrСсылкиДанных(1)            ContentOfBookmark = Worksheets("Данные для проекта").Cells(3, 3)            On Error Resume Next            UpdateBookmarks Wd, NameOfBookmark, ContentOfBookmark            Dim ContentString As String            For i = 4 To Кол_воЭл_овМассиваДанных Step 1                If Len(arrСсылкиДанных(i)) > 1 Then                   NameOfBookmark = arrСсылкиДанных(i)                   ContentString = CStr(Worksheets("БД для АОСР (2)").Cells(i, НомерСтолбца))                   If ContentString = "-" Or ContentString = "0" Then ContentString = ""                   ContentOfBookmark = ContentString                   On Error Resume Next                   UpdateBookmarks Wd, NameOfBookmark, ContentOfBookmark                End If            Next i                         Rem -= Обновляем поля, что бы ссылки в документе Word так же обновились и приняли значение закладок, на которые ссылаются =-            Wd.Fields.Update                         Rem -= Сохраняем и закрываем файл =-            Wd.SaveAs Filename:=ИмяФайла, FileFormat:=wdFormatXMLDocument            Wd.Close False: Set Wd = Nothing

Sub UpdateBookmarks(ByRef Wd, ByVal NameOfBookmark As String, ByVal ContentOfBookmark As Variant)    On Error Resume Next    Dim oRng As Variant    Dim oBm    Set oBm = Wd.Bookmarks    Set oRng = oBm(NameOfBookmark).Range    oRng.Text = ContentOfBookmark    oBm.Add NameOfBookmark, oRngEnd Sub


2. Если рисовать таблицы средствами Word, то к ним можно обращаться с адресацией в ячейку
 Rem -= Заполняем данными таблицы ЖВК =-       Dim y, k As Integer       Let k = 1       For y = Worksheets("Титул").Cells(4, 4) To Worksheets("Титул").Cells(4, 5)           Wd.Tables(3).cell(k, 1).Range.Text = Worksheets("БД для входного контроля (2)").Cells(6, 4 + y)           Let k = k + 1       Next y       End With       


Между выводами в файлы форматов Word и Excel есть огромная пропасть, которая заключается в следующем:

Шаблон Excel требует перед использованием настроить отображение под конкретный принтер, т.к. фактическая область печати разнится от модели к модели. Так же перенос строки текста возможен, но только в пределах ячейки/объединенных ячеек. В последнем случае не будте автораздвигания строки, в случае переноса текста. Т.е. Вам вручную придется заранее определит границы области, которые будут содержать текст, который в свою очередь в них еще должен убраться. Зато Вы точно задали границы печати и выводимого текста и уверены, что не съедет информация (но не содержание) с одного листа на другой.

Шаблон Word при настройке автоматически переносит текст на последующую строку, если он не убрался по ширине ячейки/строки, однако этим самым он вызывает непрогнозируемый сдвиг текста по вертикали. Учитывая тот факт, что по требованиям к Исполнительной документации в строительстве ЗАПРЕЩЕНО один акт печатать на 2х и более листах, то это в свою очередь так же рождает проблемы.


С проектом можно ознакомиться тут:
vk.com/softpto
Все программы распространяются абсолютно бесплатно. Всем Добра! ;)
Подробнее..

Из песочницы Данная ситуация выходит за рамки наших компетенций как быстро и просто остаться без ESD ключей

13.11.2020 04:15:27 | Автор: admin
ESD лицензия это отличный выбор для быстрой покупки подлинного программного обеспечения: оплатили счёт через пару часов получили заветный подлинный ключ. Но даже если вы покупаете её у официального реселлера компании Microsoft, это вовсе не гарантирует, что вы можете использовать ваш ключ тогда, когда вы этого захотите, даже если вы не нарушаете условий лицензионного соглашения. Подробнее под кат.

Наша компания занимается IT аутсорсингом предприятий. Год назад мы взяли на обслуживание одну производственную компанию, которая только что отказалась от услуг другой организации. По рассказам работников это расставание прошло не очень хорошо, были взаимные недопонимания и упрёки, тем не менее, с поставщиком рассчитались, нерешённых вопросов не осталось. Важно отметить, что предыдущая организация также являлась поставщиком оборудования и программного обеспечение, что весьма удобно.

Когда стали проводить аудит хозяйства, которое досталось нам на обслуживание, мы обратили внимание, что работники тщательно следили за лицензиями: была целая папка со всеми ключами / подписками / документами, подтверждающими право владения. Редкий случай, но тем не менее, нам это показалось большим плюсом.

Крупных инцидентов с оборудованием за время обслуживания не происходило до недавнего времени, когда потребовалось переустановить ОС на одном из компьютеров. ОС установили, ввели ESD ключ для Windows система активировалась. Установили Office 2016 Home and Business, ввели ESD ключ для Office и вот тут началось самое интересное. Мы увидели сообщение Ключ уже использован, выполните вход в учётную запись Microsoft. Ещё раз перепроверили все символы, убедились в том, что это постоянная лицензия, а не подписка. Решили войти в учётную запись MS. Перепробовав все возможные для этого действия адреса почтовых ящиков стало понятно, что учётной записи MS у организации нет.

Далее с помощью чата технической поддержки MS и специалиста по имени Jai мы решили выяснить, каким же образом был активирован Office в первый раз, если учётной записи у клиента нет. Мы предоставили ключ и возможные адреса e-mail, к которым мог быть привязан наш ключ. ВУАЛЯ! Стало понятно, что поставщик привязал проданный организации ключ к своему личному аккаунту MS. И чтобы воспользоваться Office снова, надо на целевом ПК просто войти в его учётную запись.

image

Мы связались с этим поставщиком, на что получили ожидаемый ответ, что если что-то хотите, можем за деньги приехать и сделать. А именно, войти в свою учётную запись. И это придётся делать каждый раз, когда нам потребуется переустановить Office с помощью ESD ключей, так как весь Office ESD был куплен у одного поставщика.

Руководство организации было в лёгком замешательстве, ведь ESD ключи куплены, есть все необходимые документы, но использовать эти ключи нельзя без личного оплачиваемого визита поставщика. Все участвующие в этой истории люди понимали, что это неудачное стечение обстоятельств. Поставщик и аутсорсер были одним лицом и он решил не усложнять себе жизнь созданием учётной записи для организации, а просто активировал лицензию на своей учётке. А проконтролировать правильность этих действий на стороне организации никто не мог.

В чате ТП нам помочь не смогли, так как не обладают компетенциями в этом вопросе, и дали ссылку на созданный инцидент, предложив продолжить общение через e-mail.

После нескольких писем стало понятно, что никто на этом свете не может решить проблему с переносом ключей с одной учётной записи на другую. Вот основные выдержки из писем MS (я убрал множественные извинения за предоставленные неудобства):

Письмо 1
При проверке учетной записи XXXX@XXXX.ru (прим. почта поставщика) не было выявлено препятствий по ее использованию. Для нее действительно активирована упомянутая лицензия. Обращаем Ваше внимание, что в данном случае не представляется возможным перенести лицензию на другой аккаунт, поскольку ключ продукта привязан к конкретной учетной записи, и она функциональна.

Письмо 2
Исходя из информации в Вашем письме, закономерным решением является связаться с партнером по месту приобретения лицензии на основании официального договора. Как Вы правильно заметили, ключ продукта привязан к конкретной учетной записи и к ней должен быть доступ. Со своей стороны, мы не имеем права предоставлять рекомендации по восстановлению доступа к аккаунту, поскольку он принадлежит другому пользователю.

Письмо 3
Мы всегда на стороне клиентов, но данная ситуация выходит за рамки наших компетенций, поэтому рекомендовали обратиться в соответствующую службу поддержки со страницы support.microsoft.com/ru-ru/contactus или support.microsoft.com, используя шаги из предыдущих писем.
Благодарим за понимание.

Как вы понимаете, нас отправили обратно на страницу ТП, из которой нас ранее отправили на общение по e-mail.

Что мы имеем в сухом остатке


  • Ключи ESD куплены, но воспользоваться ими нельзя.
  • Поставщик предлагает за деньги активировать уже купленный Office, потому что сделать это без него специалисты компании не могут.
  • Сам поставщик не имеет технической возможности отвязать от своей учётной записи ключи. Или он может просто не хотеть этого делать, даже если бы возможность была.
  • Компания Microsoft тоже не имеет возможности перенести ключи из одной учётной записи в другую.

Особо важно отметить, что в этой ситуации никто кроме конечных покупателей, купивших подлинный продукт, не оказался в минусе. Поставщик (являющийся официальным ресселером официального дистрибьютора в России 1С-Гендальф) продолжает продавать лицензии.

Но если действия поставщика ещё можно списать на какие-то человеческие факторы, то удручает тот факт, что Microsoft не только не может / не хочет применять какие-то меры по отношению к недобросовестным поставщикам, которые портят им репутацию, но и не может решить техническую проблему (перенос ключей) на своей стороне.

Будьте внимательны с ESD лицензиями и выбором поставщиков!
Подробнее..

Конвертируем doc в docx и xml на C

23.11.2020 10:05:21 | Автор: admin

Продолжаю свой цикл статей, посвященный конвертации различных текстовых файлов с помощью решений, реализованных на языке C#.


С момента моей последней публикации Конвертация xls в xlsx и xml на C# прошло более полугода, за которые я успел сменить как работодателя, так и пересмотреть свои взгляды на некоторые аспекты коммерческой разработки. Сейчас, работая в международной компании с совершенно иным подходом к разработке ПО (ревью кода, юнит-тестирование, команда автотестеров, строгое соблюдение СМК, заботливый менеджер, очаровательная HR и прочие корпоративные плюшки), я начинаю понимать, почему некоторые из комментаторов интересовались целесообразностью предлагаемых мной велокостылей, когда на рынке есть очень достойные готовые решения, например, от e-iceblue. Но давайте не забывать, что ситуации бывают разные, компании тем более, и если потребность в решении какой-то задачи с использованием определенного инструментария возникла у одного человека, то со значительной долей вероятности она возникнет и у другого.



Итак, дано:


  1. Неопределенное множество файлов в формате .doc, которые нужно конвертировать в xml (например, для парсинга и организации автоматизированной навигации внутри текста), желательно с сохранением форматирования.
  2. На сервере памяти чуть больше, чем у рыбки, а на процессоре уже можно жарить яичницу, да и у компании нет лишней лицензии на Word, поэтому конвертация должна происходить без запуска каких-либо офисных приложений.
  3. Сервис должен быть написан на языке C# и в последующем интегрирован в код другого продукта.
  4. На решение задачи два дня и две ночи, которые истекли вчера.

Поехали!


  • Во-первых, нужно сразу уяснить, что старые офисные форматы файлов, такие как .doc и .xls, являются бинарными, и достать что-нибудь человекочитаемое из них без использования текстовых редакторов/процессоров не получится. Прочитать об этом можно в официальной документации. Если есть желание поковыряться поглубже, посчитать нолики с единичками и узнать, что они означают, то лучше сразу перейти сюда.
  • Во-вторых, несмотря на наличие бесплатных решений для работы с .doc, большинство из них написаны на Python, Ruby и чем угодно еще, но не C#.
  • В-третьих, найденное мной решение, а именно библиотека b2xtranslator, является единственным доступным бесплатным инструментом такого рода, еще и написана при поддержке Microsoft, если верить вот этому источнику. Если вдруг вы встречали какие-нибудь аналоги данной библиотеки, пожалуйста, напишите об этом в комментариях. Даже это душеспасительное решение не превратит .doc в .xml, однако поможет нам превратить его в .docx, с которым мы уже умеем работать.

Довольно слов давайте к делу


Установка b2xtranslator


Для работы нам понадобиться библиотека b2xtranslator. Ее можно подключить через менеджера пакетов NuGet.

Однако я настоятельно рекомендую скачать ее из официального git-репозитория по следующим причинам:


  • a) Библиотека представляет собой комбайн, работающий с различными бинарными офисными документами (.doc, .xls, .ppt), что может быть избыточным
  • b) Проект достаточно долго не обновляется и вам, возможно, придется доработать его напильником
  • c) Задача, с которой я столкнулся, как раз потребовала внесения некоторых изменений в работу библиотеки, а также изучения ее алгоритмов и используемых структур для успешной интеграции в свое решение
    Для дальнейшей работы нам понадобиться подключить в свое решение два проекта из библиотеки: b2xtranslator\Common\b2xtranslator.csproj и b2xtranslator\Doc\b2xtranslator.doc.csproj

Конвертация .doc в .docx


Конвертация документов строится по следующему алгоритму:


  1. Инициализация дескриптора для конвертируемого файла.
    Для этого необходимо создать экземпляр класса StructuredStorageReader, конструктор которого в качестве аргумента может принимать или путь до файла, или последовательность байтов (Stream), что делает его крайне удобным при работе с файлами, загружаемыми по сети. Также обращаю внимание, что так как библиотека b2xtranslator является комбайном для конвертации бинарных офисных форматов в современный OpenXML, то независимо от того, какой формат мы хотим конвертировать (.ppt, .xls или .doc) инициализация дескриптора всегда будет происходить с помощью указанного класса (StructuredStorageReader).
    StructuredStorageReader reader = new StructuredStorageReader(docPath);
    
  2. Парсинг бинарного .doc файла с помощью объекта класса WordDocument, конструктор которого в качестве аргумента принимает объект типа StructuredStorageReader.
    WordDocument doc = new WordDocument(reader);
    
  3. Создание объекта, который будет хранить данные для файла в формате .docx.
    Для этого используется статический метод cs public static WordprocessingDocument Create(string fileName, OpenXmlPackage.DocumentType type) класса WordprocessingDocument. В первом аргументе указываем имя нового файла (вместе с путем), а вот во втором мы должны выбрать тип файла, который должен получиться на выходе:
    a. Document (обычный документ с расширением .docx);
    b. MacroEnabledDocument (файл, содержащий макросы, с расширением .docm);
    c. Template (файл шаблонов word с расширением .dotx);
    d. MacroEnabledTemplate (файл с шаблоном word, содержащий макросы. Имеет расширение .dotm).
    WordprocessingDocument docx = WordprocessingDocument.Create(docxPath, DocumentType.Document);
    
  4. Конвертация данных из бинарного формата в формат OpenXML и их запись в объект типа WordprocessingDocument.
    За выполнение указанной процедуры отвечает статический метод
    public static void Convert(WordDocument doc, WordprocessingDocument docx)
    

    класса Converter, который заодно и записывает получившийся результат в файл.

    Converter.Convert(doc, docx);
    

    В результате у вас должен получиться вот такой код:

    using b2xtranslator.StructuredStorage.Reader;using b2xtranslator.DocFileFormat;using b2xtranslator.OpenXmlLib.WordprocessingML;using b2xtranslator.WordprocessingMLMapping;using static b2xtranslator.OpenXmlLib.OpenXmlPackage;namespace ConverterToXml.Converters{    public class DocToDocx    {        public void ConvertToDocx(string docPath, string docxPath)        {            StructuredStorageReader reader = new StructuredStorageReader(docPath);            WordDocument doc = new WordDocument(reader);            WordprocessingDocument docx = WordprocessingDocument.Create(docxPath, DocumentType.Document);            Converter.Convert(doc, docx);        }    }}
    

    Внимание!
    Если вы используете платформу .Net Core 3 и выше в своем решении, обратите внимание на целевые среды для подключенных проектов b2xtranslator. Так как библиотека была написана довольно давно и не обновляется с 2018 года, по умолчанию она собирается под .Net Core 2.
    Чтобы сменить целевую среду, щелкните правой кнопкой мыши по проекту, выберите пункт Свойства и поменяйте целевую рабочую среду. В противном случае вы можете столкнуться с проблемой невозможности конвертации файлов .doc, содержащих в себе таблицы.
    Я не стал разбираться, почему так происходит, но энтузиастам могу подсказать, что причину стоит искать в 40 строчке файла ~\b2xtranslator\Doc\WordprocessingMLMapping\MainDocumentMapping.cs в момент обработки таблицы.
    Кроме того, рекомендую собирать все проекты и само решение под 64-битную платформу во избежание всяких непонятных ошибок.



    Сохранение результата в поток байтов


    Так как моей целью при использовании данного решения была конвертация .doc в .xml, а не в .docx, предлагаю вовсе не сохранять промежуточный OpenXML файл, а записать его в виде потока байтов. К сожалению, b2xtranslator не предоставляет нам подходящих методов, но это довольно легко исправить:
    В абстрактном классе OpenXmlPackage (см. ~\b2xtranslator\Common\OpenXmlLib\OpenXmlPackage.cs) давайте создадим виртуальный метод:


    public virtual byte[] CloseWithoutSavingFile(){    var writer = new OpenXmlWriter();    MemoryStream stream = new MemoryStream();    writer.Open(stream);    this.WritePackage(writer);    writer.Close();    byte[] docxStreamArray = stream.ToArray();    return docxStreamArray;}
    

    По большому счету, данный метод будет заменять собой метод Close(). Вот его исходный код:


    public virtual void Close(){     // serialize the package on closing    var writer = new OpenXmlWriter();    writer.Open(this.FileName);    this.WritePackage(writer);    writer.Close();}
    

    Скажем спасибо разработчикам библиотеки за то, что не забыли перегрузить метод Open(), который может принимать или имя файла, или поток байтов. Однако, библиотечный метод Close(), который как раз и отвечает за запись результата в файл, вызывается в методе Dispose() в классе OpenXmlPackage. Чтобы ничего лишнего не поломать и не заморачиваться с архитектурой фабрик (тем более в чужом проекте), я предлагаю просто закомментировать код внутри метода Dispose() и вызвать метод CloseWithoutSavingFile(), но уже внутри нашего метода после вызова Converter.Convert(doc, docx).
    Для сохранения результата конвертации вызываем вместо docx.Close() метод docx.CloseWithoutSavingFile():


    public MemoryStream ConvertToDocxMemoryStream(Stream stream){    StructuredStorageReader reader = new StructuredStorageReader(stream);    WordDocument doc = new WordDocument(reader);    var docx = WordprocessingDocument.Create("docx", DocumentType.Document);    Converter.Convert(doc, docx);    return new MemoryStream(docx.CloseWithoutSavingFile());}
    

    Теперь библиотека b2xtranslator будет возвращать сконвертированный из формата .doc в .docx файл в виде потока байтов. Даже если у вас нет цели получить на выходе .xml, такой метод может оказаться более подходящим для дальнейшей работы с файлами, тем более что стрим всегда можно сохранить в виде файла там, где вам надо.
    Для тех, кому все-таки очень хочется получить на выходе .xml документ, еще и с сохраненной структурой, предлагаю дойти до кухни, сварить кофе покрепче, добавить в него рюмку коньяка и приготовиться к приключению на 20 минут.


    Конвертация .doc в .xml



    Теперь, когда, казалось бы, можно воспользоваться классом-конвертором DocxToXml, работа которого была описана вот в этой статье, нас поджидает сюрприз, связанный с особенностями работы b2xtranslator.
    Давайте посмотрим на результат работы библиотеки повнимательнее и сравним с оригинальным .docx файлом, из которого был экспортирован .doc файл для конвертации. Для этого достаточно изменить расширение сравниваемых файлов с .docx на .zip. Вот отличия, которые мы увидим, заглянув внутрь архивов:


    1. В результате конвертации в новом .docx файле (справа) отсутствуют папки customXml и docProps.
    2. Внутри папки word, мы также найдем определенные отличия, перечислять которые я, конечно же, не буду:
    3. Естественно, что и метаданные, по которым осуществляется навигация внутри документа, также отличаются. Например, на представленном скрине и далее оригинальный .docx слева, сгенерированный b2xtranslator cправа.

      Налицо явное отличие в атрибутах тега w:document, но этим отличия не заканчиваются. Всю "мощь" библиотеки мы ощутим, когда захотим обработать списки и при этом:
      a. Сохранить их нумерацию
      b. Не потерять структуру вложенности
      c. Отделить один список от другого

    Давайте сравним файлы document.xml для вот этого списка:


    1.1 Первый.Первый1.2 Первый.Второй1.2.1   Первый.Второй.Первый1.2.2   Первый.Второй.ВторойКакая-то строчка 1.2.3   Первый.Второй.Третий2.  Второй2.1 Второй.Первый
    

    Вот так будет выглядеть .xml для первого элемента списка.


    -Во-первых, мы видим, что сама структура документов несколько отличается (например, точка внутри строк рассматривается как отдельный элемент, что, как оказалось, совсем не страшно).
    -Во-вторых, у тегов остался только один атрибут (w:rsidR), а вот w:rsidR, w14:textId, w:rsidRDefault, w:paraId и w:rsidP пропали. Все эти особенности приводят к тому, что наш класс-конвертер DocxToXml(про него подробно можно почитать здесь) подавится и поднимет лапки вверх с ошибкой NullReferenceException, что указывает на отсутствие индексирования параграфов внутри документа.

    Вместе с тем, если мы попытаемся такой файл отрыть в Word, то увидим, что все хорошо отображается, а таблицы и списки покоятся на своих местах! Магия!
    В общем, когда в поисках решения я потратил N часов на чтение документации, мои красные от дебагера глаза омылись горькими слезами, а один лишь запах кофе стремился показать коллегам мой дневной рацион, решение было найдено!
    Исходя из документации к формату doc и алгоритмов работы b2xtranslator, можно сделать вывод, что исторически в бинарных офисных текстовых документах отсутствовала индексация по параграфам*. Возникает задача расставить необходимые теги в нужных местах.
    За индекс параграфа отвечает атрибут тега paraId, о чем прямо написано здесь. Данный атрибут относится к пространству имен w14, о чем можно догадаться при изучении document.xml из архива .docx. В принципе, на скринах выше вы это тоже видите. Объявление пространства имен в .xml выглядит так:


    xmlns:wp14="http://personeltest.ru/away/schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
    

    Теперь давайте заставим b2xtranslator добавлять это пространство имен и идентификатор каждому параграфу. Для этого в файле ~\b2xtranslator\Common\OpenXmlLib\ContentTypes.cs после 113 строки добавим вот эту строчку:


    public const string WordprocessingML2010 = "http://schemas.microsoft.com/office/word/2010/wordml";
    

    Кстати, если посмотрите на комментарии в коде, то увидите, что в этом блоке как раз располагаются поддерживаемые пространства имен для вордовых документов:

    Далее наша задача заставить библиотеку вставлять в начало файла ссылку на данное пространство имен. Для этого в файле ~\b2xtranslator\Doc\WordprocessingMLMapping\MainDocumentMapping.cs в 24 строке вставим код:


    this._writer.WriteAttributeString("xmlns", "w14", null, OpenXmlNamespaces.WordprocessingML2010);
    

    Разработчики библиотеки также позаботились о документации:


    Теперь дело за малым заставить b2xtranslator индексировать параграфы. В качестве индексов предлагаю использовать рандомно сгенерированные GUID может быть, это несколько тяжеловато, но зато надежно!

    Переходим в файл ~\b2xtranslator\Doc\WordprocessingMLMapping\DocumentMapping.cs и в 504 и 505 строки вставляем вот этот код:


    this._writer.WriteAttributeString("w14", "paraId", OpenXmlNamespaces.WordprocessingML2010, Guid.NewGuid().ToString());            this._writer.WriteAttributeString("w14", "textId", OpenXmlNamespaces.WordprocessingML2010, "77777777");
    

    Что касается второй строчки, в которой мы добавляем каждому тегу параграфа атрибут w14:textId = "77777777", то тут можно лишь сказать, что без этого атрибута ничего работать не будет. Для пытливых умов вот ссылка на документацию.
    Если серьезно, то, как я понимаю, атрибут используется, когда текст разделен на разные блоки, внутри которых происходит индексация тегов, которые могут иметь одинаковый Id внутри одного документа. Видимо, для этих случаев используется дополнительная индексация текстовых блоков. Однако, так как мы используем GUID, который в несколько раз больше индексов, используемых в вордовских документах по умолчанию, то генерацией отдельных индексов для текстовых блоков можно и пренебречь.


    Вот теперь мы получили .docx-файл, пригодный для дальнейшего преобразования в .xml. Подробнее о том, как работать с ним дальше, вы можете прочитать в этой статье или воспользоваться уже выложенным на github решением.


    В заключение, если у вас на проекте есть возможность воспользоваться платным надежным софтом, то этот путь скорее всего не для вас. Однако же, если вы энтузиаст, пишете свой pet-проект и уважительно относитесь к авторским правам, а также если ваш проект находится в стадии прототипирования и пока не готов к покупке дорогостоящих лицензий, а разработку продолжать надо, то, мне кажется, этот вариант может вам очень даже подойти! Тем более, что у вас есть возможность воспользоваться готовым решением и не заливать свои краснющие зенки визином, изучая документацию и особенности работы некоторых, на первый взгляд, сомнительных решений.


    Наконец, бонус для тех, кто хочет разобраться, что значат все эти бесконечные теги и их атрибуты в документах .docx и как они мапаются на бинарный .doc: советую заглянуть в файл ~\b2xtranslator\Doc\DocFileFormat\CharacterProperties.cs, а также посмотреть спецификацию для docx и doc.

Подробнее..

Исследование качества кода Open XML SDK от Microsoft

27.11.2020 10:16:55 | Автор: admin
image1.png

Моё знакомство с Open XML SDK началось с того, что мне понадобилась библиотека для создания документов Word с некоторой отчётностью. После работы с Word API более 7 лет, захотелось попробовать что-нибудь новое и более удобное. Так я узнал, что у Microsoft есть альтернативное решение. По традиции, используемые в компании программы и библиотеки мы предварительно проверяем с помощью анализатора PVS-Studio.

Введение


Office Open XML, также известный как OpenXML или OOXML, представляет собой формат на основе XML для офисных документов, включая текстовые документы, электронные таблицы, презентации, а также диаграммы, фигуры и другой графический материал. Спецификация была разработана Microsoft и принята ECMA International в 2006 году. В июне 2014 года Microsoft выпустила Open XML SDK в open source. Сейчас исходники доступны на GitHub под лицензий MIT.

Для поиска ошибок в исходном коде библиотеки использовался PVS-Studio. Это инструмент для выявления ошибок и потенциальных уязвимостей в исходном коде программ, написанных на языках С, C++, C# и Java. Работает в 64-битных системах на Windows, Linux и macOS.

Проект достаточно маленький и предупреждений нашлось немного. Но выбор титульной картинки основывался как раз на результатах. Уж очень много бесполезных условных операторов в коде нашлось. Мне кажется, если отрефакторить все такие места в коде, то объём заметно сократится. Вследствие чего ещё и читаемость кода повысится.

Почему Word API, а не Open XML SDK


Как Вы могли догадаться из заголовка, я продолжил использовать Word API. У этого способа достаточно много минусов:

  • Старый неудобный API;
  • Должен быть установлен Microsoft Office;
  • Необходимость распространять дистрибутив с библиотеками Office;
  • Зависимость работы Word API от настроек локали системы;
  • Низкая скорость работы.

С локалью вообще забавный случай произошёл. В Windows есть десяток региональных настроек. На одном из серверных компьютеров оказалась каша из локалей USA и UK. Из-за этого создавались документы Word, где вместо символа доллара был рубль, а фунты вообще не выводились. Доработка настроек операционной системы исправила проблему.

Перечисляя всё это, я снова задумался, почему я до сих пор этим пользуюсь

Но нет, Word API мне пока нравится больше, и вот почему.

OOXML выглядит таким образом:

<?xml version="1.0" encoding="utf-8" standalone="yes"?><w:document ....>  <w:body>    <w:p w:rsidR="00E22EB6"         w:rsidRDefault="00E22EB6">      <w:r>        <w:t>This is a paragraph.</w:t>      </w:r>    </w:p>    <w:p w:rsidR="00E22EB6"         w:rsidRDefault="00E22EB6">      <w:r>        <w:t>This is another paragraph.</w:t>      </w:r>    </w:p>  </w:body></w:document>

Где <w:r> (Word Run) не предложение, и даже не слово, а любой фрагмент текста, имеющий атрибуты, отличные от соседних фрагментов текста.

Программируется это примерно таким кодом:

Paragraph para = body.AppendChild(new Paragraph());Run run = para.AppendChild(new Run());run.AppendChild(new Text(txt));

У документа специфичная внутренняя структура, и в коде нужно создавать те же самые элементы. У Open XML SDK, я считаю, недостаточно абстрактный уровень доступа к данным. Создание документа с помощью Word API будет более понятым и коротким. Особенно, когда дело дойдёт до таблиц и других сложных структур данных.

В свою очередь, Open XML SDK решает большой ряд задач. С ним можно создавать документы не только для Word, но и для Excel и PowerPoint. Наверное, для некоторых задач эта библиотека больше подходит, но я решил пока остаться на Word API. Полностью от него отказаться в любом случае не получится, т.к. для внутренних нужд мы разрабатываем плагин для Word, а там возможно использование только Word API.

Два значения для string


V3008 The '_rawOuterXml' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 164, 161. OpenXmlElement.cs 164

internal string RawOuterXml{    get => _rawOuterXml;    set    {        if (string.IsNullOrEmpty(value))        {            _rawOuterXml = string.Empty;        }        _rawOuterXml = value;    }}

Тип string может иметь 2 типа значений: null и текстовое значение. Использовать текстовое значение определённо безопаснее, но оба подхода имеют права на существование. Вот в этом проекте значение null использовать неприемлемо и его перезаписывают на string.Empty по крайней мере, так задумывалось. Но из-за ошибки в RawOuterXml всё же можно записать null, а потом обратиться к этому полю, получив NullReferenceException.

V3022 Expression 'namespaceUri != null' is always true. OpenXmlElement.cs 497

public OpenXmlAttribute GetAttribute(string localName, string namespaceUri){    ....    if (namespaceUri == null)    {        // treat null string as empty.        namespaceUri = string.Empty;    }    ....    if (HasAttributes)    {        if (namespaceUri != null)  // <=        {            ....        }        ....    }    ....}

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

Про компактность кода


image2.png

V3009 It's odd that this method always returns one and the same value of '".xml"'. CustomXmlPartTypeInfo.cs 31

internal static string GetTargetExtension(CustomXmlPartType partType){    switch (partType)    {        case CustomXmlPartType.AdditionalCharacteristics:            return ".xml";        case CustomXmlPartType.Bibliography:            return ".xml";        case CustomXmlPartType.CustomXml:            return ".xml";        case CustomXmlPartType.InkContent:            return ".xml";        default:            return ".xml";    }}

Не знаю, имеет ли место тут какая-нибудь опечатка или просто автор кода написал "красивый" код по его мнению. Я уверен, что нет смысла возвращать из функции столько однотипных значений и код можно сильно сократить.

Это не единственное такое место. Вот ещё парочка таких предупреждений:

  • V3009 It's odd that this method always returns one and the same value of '".xml"'. CustomPropertyPartTypeInfo.cs 25
  • V3009 It's odd that this method always returns one and the same value of '".bin"'. EmbeddedControlPersistenceBinaryDataPartTypeInfo.cs 22

Интересно было бы услышать, зачем так писать.

V3139 Two or more case-branches perform the same actions. OpenXmlPartReader.cs 560

private void InnerSkip(){    Debug.Assert(_xmlReader != null);    switch (_elementState)    {        case ElementState.Null:            ThrowIfNull();            break;        case ElementState.EOF:            return;        case ElementState.Start:            _xmlReader.Skip();            _elementStack.Pop();            GetElementInformation();            return;        case ElementState.End:        case ElementState.MiscNode:            // cursor is end element, pop stack            _xmlReader.Skip();            _elementStack.Pop();            GetElementInformation();            return;        ....    }    ....}

К этому коду возникает меньше вопросов. Скорее всего, идентичные кейсы можно объединить и код станет короче и очевиднее.

Ещё несколько таких мест:

  • V3139 Two or more case-branches perform the same actions. OpenXmlMiscNode.cs 312
  • V3139 Two or more case-branches perform the same actions. CustomPropertyPartTypeInfo.cs 30
  • V3139 Two or more case-branches perform the same actions. CustomXmlPartTypeInfo.cs 15
  • V3139 Two or more case-branches perform the same actions. OpenXmlElement.cs 1803

Те самые Always true/false


Настало время привести примеры кода, которые определили мой выбор титульной картинки.

Предупреждение 1

V3022 Expression 'Complete()' is always false. ParticleCollection.cs 243

private bool IsComplete => Current is null ||                           Current == _collection._element.FirstChild;public bool MoveNext(){    ....    if (IsComplete)    {        return Complete();    }    if (....)    {        return Complete();    }    return IsComplete ? Complete() : true;}

Свойство IsComlete используется 2 раза, и по коду легко понять, что его значение не изменится. Таким образом, в конце функции можно просто возвращать второе значение тернарного оператора true.

Предупреждение 2

V3022 Expression '_elementStack.Count > 0' is always true. OpenXmlDomReader.cs 501

private readonly Stack<OpenXmlElement> _elementStack;private bool MoveToNextSibling(){    ....    if (_elementStack.Count == 0)    {        _elementState = ElementState.EOF;        return false;    }    ....    if (_elementStack.Count > 0) // <=    {        _elementState = ElementState.End;    }    else    {        // no more element, EOF        _elementState = ElementState.EOF;    }    ....}

Очевидно, что если в стеке_elementStack не 0 элементов, то их больше. Код можно сократить как минимум на 8 строк.

Предупреждение 3

V3022 Expression 'rootElement == null' is always false. OpenXmlPartReader.cs 746

private static OpenXmlElement CreateElement(string namespaceUri, string name){    if (string.IsNullOrEmpty(name))    {        throw new ArgumentException(....);    }    if (NamespaceIdMap.TryGetNamespaceId(namespaceUri, out byte nsId)        && ElementLookup.Parts.Create(nsId, name) is OpenXmlElement element)    {        return element;    }    return new OpenXmlUnknownElement();}private bool ReadRoot(){  ....  var rootElement = CreateElement(....);  if (rootElement == null) // <=  {      throw new InvalidDataException(....);  }  ....}

Функция CreateElement не может вернуть null. Если в компании было принято правило писать методы для создания xml-нод, которые либо возвращают валидный объект, либо кидают исключение, то пользователям таких функций можно не злоупотреблять дополнительными проверками.

Предупреждение 5

V3022 Expression 'nameProvider' is always not null. The operator '?.' is excessive. OpenXmlSimpleTypeExtensions.cs 50

public static XmlQualifiedName GetSimpleTypeQualifiedName(....){    foreach (var validator in validators)    {        if (validator is INameProvider nameProvider &&            nameProvider?.QName is XmlQualifiedName qname) // <=        {            return qname;        }    }    return type.GetSimpleTypeQualifiedName();}

Оператор is имеет такой паттерн:

expr is type varname

Если выражение expr принимает значение true, то объект varname будет валидным и его не надо снова сравнивать с null, как это сделано в этом фрагменте кода.

Предупреждение 6

V3022 Expression 'extension == ".xlsx" || extension == ".xlsm"' is always false. PresentationDocument.cs 246

public static PresentationDocument CreateFromTemplate(string path){    ....    string extension = Path.GetExtension(path);    if (extension != ".pptx" && extension != ".pptm" &&        extension != ".potx" && extension != ".potm")    {        throw new ArgumentException("...." + path, nameof(path));    }    using (PresentationDocument template = PresentationDocument.Open(....)    {        PresentationDocument document = (PresentationDocument)template.Clone();        if (extension == ".xlsx" || extension == ".xlsm")        {            return document;        }        ....    }    ....}

Интересный код получился. Сначала автор отсеял все документы с расширениями не .pptx, .pptm, .potx и .potm, а потом решил для перестраховки проверить, что среди них нет .xlsx и .xlsm. Функция PresentationDocument определённо жертва рефакторинга.

Предупреждение 7

V3022 Expression 'OpenSettings.MarkupCompatibilityProcessSettings == null' is always false. OpenXmlPackage.cs 661

public MarkupCompatibilityProcessSettings MarkupCompatibilityProcessSettings{    get    {        if (_mcSettings is null)        {            _mcSettings = new MarkupCompatibilityProcessSettings(....);        }        return _mcSettings;    }    set    {        _mcSettings = value;    }}public MarkupCompatibilityProcessSettings MarkupCompatibilityProcessSettings{    get    {        if (OpenSettings.MarkupCompatibilityProcessSettings == null) // <=        {            return new MarkupCompatibilityProcessSettings(....);        }        else        {            return OpenSettings.MarkupCompatibilityProcessSettings;        }    }}

Свойство MarkupCompatibilityProcessSettings никогда не возвращает null. Если в геттере выясняется, что поле класса имеет значение null, то объект перезаписывается на новый. Ещё обратите внимание, что это не рекурсивный вызов одного свойства, а это одноимённые свойства из разных классов. Возможно, некоторая путаница и привела к написанию лишних проверок.

Остальные предупреждения


Предупреждение 1

V3080 Possible null dereference. Consider inspecting 'previousSibling'. OpenXmlCompositeElement.cs 380

public OpenXmlElement PreviousSibling(){    if (!(Parent is OpenXmlCompositeElement parent))    {        return null;    }    ....}public override T InsertBefore<T>(T newChild, OpenXmlElement referenceChild){    ....    OpenXmlElement previousSibling = nextNode.PreviousSibling();    prevNode.Next = nextNode;    previousSibling.Next = prevNode;    // <=    ....}

А вот пример, где дополнительной проверки как раз не хватает. Метод PreviousSibling как раз может вернуть значение null, а результат этой функции сразу используется без проверки.

Ещё 2 опасных места:

  • V3080 Possible null dereference. Consider inspecting 'prevNode'. OpenXmlCompositeElement.cs 489
  • V3080 Possible null dereference. Consider inspecting 'prevNode'. OpenXmlCompositeElement.cs 497

Предупреждение 2

V3093 The '&' operator evaluates both operands. Perhaps a short-circuit '&&' operator should be used instead. UniqueAttributeValueConstraint.cs 60

public override ValidationErrorInfo ValidateCore(ValidationContext context){    ....    foreach (var e in root.Descendants(....))    {        if (e != element & e.GetType() == elementType) // <=        {            var eValue = e.ParsedState.Attributes[_attribute];            if (eValue.HasValue && _comparer.Equals(....))            {                return true;            }        }    }    ....}

Некоторые любят применять оператор '&' к логическим выражениям там, где не надо. В случае этого оператора сначала вычисляется второй операнд, независимо от результата первого. Здесь это не сильно серьёзная ошибка, но такой неаккуратный код после рефакторинга может приводить и к потенциальным исключениям NullReferenceException.

Предупреждение 3

V3097 Possible exception: type marked by [Serializable] contains non-serializable members not marked by [NonSerialized]. OpenXmlPackageValidationEventArgs.cs 15

[Serializable][Obsolete(ObsoleteAttributeMessages.ObsoleteV1ValidationFunctionality, false)][EditorBrowsable(EditorBrowsableState.Never)]public sealed class OpenXmlPackageValidationEventArgs : EventArgs{    private string _message;    [NonSerialized]    private readonly object _sender;    [NonSerialized]    private OpenXmlPart _subPart;    [NonSerialized]    private OpenXmlPart _part;    ....    internal DataPartReferenceRelationship        DataPartReferenceRelationship { get; set; } // <=}

Сериализация класса OpenXmlPackageValidationEventArgs может стать неудачной из-за того, что одно из свойств забыли пометить несериализуемым. Либо необходимо доработать возвращаемый тип этого свойства до сериализуемого, иначе может возникать исключение в рантайме.

Заключение


Мы в компании любим проекты и технологии Microsoft. В разделе, где мы перечисляем Open Source проекты, проверенные с помощью PVS-Studio, мы даже выделили для Microsoft отдельный раздел. Там уже накопился 21 проект, про которые написано 26 статей. Это 27-я.

Уверен, Вас интересует, есть ли среди наших клиентов Microsoft. Ответ да! Но не будем забывать, что это огромная корпорация, ведущая разработку по всему миру. Определённо есть подразделения, которые уже используют PVS-Studio в своих проектах, но тех, которые не используют, ещё больше! И наш опыт работы с открытыми проектами показывает, что им явно не хватает хорошего инструмента для поиска ошибок ;).


Ещё из новостей, кому интересен анализ кода на C++, C# и Java: мы недавно добавили поддержку стандарта OWASP и активно увеличиваем его покрытие.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. Analyzing the Code Quality of Microsoft's Open XML SDK.
Подробнее..

Менеджмент будущего. Без начальников, переработок и KPI

05.01.2021 18:18:32 | Автор: admin

Нанимай людей сильнее себя, сам решай, над чем работать, отдыхай за счёт компании, работай без начальника и другие удивительные принципы компания Valve опубликовала в своем руководстве для новых сотрудников.

Кто такие Valve?

Valve Corporation американская компания-разработчик компьютерных игр. Автор хитов Conter-Strike, Left for Dead, Dota 2, создатель магазина игр Steam и очков виртуальной реальности Steam Index. Основана в 1996 году бывшими сотрудниками Microsoft Гэйбом Ньюэллом и Майком Харрингтоном.

В 2017 Forbes подсчитал, что основатель Valve Гэйб Ньюэлл с состоянием в $4,1 млрд. обогнал самого Дональда Трампа с его $3,7 млрд. При этом основной доход Ньюэлла составляет прибыль от магазина игр Steam, в том числе от покупок геймеров в Dota 2 и Counter Strike: Global Offensive.

Компания Valve коммерчески успешна, востребована и занимает лидирующие позиции в отрасли уже много лет. При этом принципы и подходы к управлению кардинально отличаются от общепринятых. Возможно, секрет успеха именно в них?

все иллюстрации взяты из руководства для новых сотрудников, опубликованного на сайте Valveвсе иллюстрации взяты из руководства для новых сотрудников, опубликованного на сайте Valve

Иерархия идеальна для стабильных и предсказуемых результатов, именно поэтому она так активно применяется в армии. Но если вы работаете в сфере развлечений и привлекаете самых одаренных людей на планете, то усадив их за стол и сказав делать, что велено, вы даром растратите 99% их ценности, убеждены в Valve.

Нам нужны новаторы, а значит, мы должны создать условия, в которых они будут процветать.

В Valve отсутствует классический формат отношений руководитель-подчиненный. Равноправие устраняет преграды между работой и потребителем, который пользуется ее результатами.

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

Не смотря на отсутствие традиционной орг.структуры, сотрудники самоорганизуются в проектные группы. Внутри группы они выбирают временного лидера и распределяют роли между собой. Структура проектной группы существует ровно столько, сколько длится проект, но не дольше. Иначе со временем группа начинает преследовать собственные интересы, а не интересы клиентов Valve.

Принцип 2. Выбери проект по душе или придумай свой

В компаниях типа Google сотрудникам дают право посвящать до 10% времени личным проектам. В компании Valve этот процент равен 100.

Поскольку в Valve все равны, никто не присоединяется к проекту по указанию сверху. Каждый сам решает, чем он будет заниматься но лишь после того, как задаст себе правильные вопросы:

  • В каком из проектов я буду наиболее полезен?

  • Какой проект больше влияет на потребителей?

  • От работы над какими задачами я получу удовольствие?

Внутри компании люди постоянно нанимаются друг к другу на работу. Компания ведет списки текущих проектов, но по мнению Valve лучший способ всё узнать это спросить коллег.

Принцип 3. У столов должны быть колесики

Т.к. сотрудники сами решают в каком проекте участвовать, каждый рабочий стол оборудован колесиками, чтобы сотрудник мог быстро присоединиться к новой рабочей группе.

Ротация столов происходит так часто, что порой отыскать нужного коллегу затруднительно. Поэтому на внутреннем портале Valve создали виртуальную онлайн-карту, где по IP компьютера можно отыскать рабочее место нужного сотрудника.

Принцип 4. Право на ошибку

Предоставление права на ошибку важная особенность компании: невозможно ожидать от сотрудников многого, при этом наказывая их за ошибки.

Даже когда сотрудники совершают ошибки, которые дорого обходятся компании или ее репутации, мы воспринимаем это как возможность чему-нибудь научиться.

Тем не менее, ошибка ошибке рознь. Например, не стоит раз за разом наступать на одни и те же грабли. Никогда не закрывайте глаза на факты, особенно если они утверждают, что вы неправы.

Принцип 5. Обратная связь всему голова

Как устроен карьерный рост в Valve? Как повышать кому-то зарплату в компании, где все равны?

В Valve существует два официальных способа оценки друг друга: рецензирование и ранжирование.

Рецензирование это сбор отзывов о совместной работе. Группа людей, состав которой регулярно меняется, проводит опрос: с кем каждый сотрудник работал в последнее время и каковы впечатления от совместной работы. Отзывы обрабатываются и передаются анонимно.

Ранжирование и оплата труда

Прибыль компании в пересчете на сотрудника превышает аналогичные показатели Google, Amazon и Microsoft. В связи с этим Valve считает справедливым платить своим людям выше рынка.

Ранжирование призвано определить кто в компании вносит больший вклад, и привести зарплату сотрудника в соответствие с его фактической ценностью.

Раз в году участники проектной группы ранжируют каждого члена команды относительно других. Само ранжирование основывается на следующих четырех показателях:

1. Профессиональный уровень/технические навыки

2. Продуктивность/результативность работы

3. Роль в команде

4. Вклад в разработку продукта

Люди ранжируют всех членов команды, кроме самого себя. Им нужно расположить своих коллег по величине вклада от большего к меньшему по каждому из 4 параметров.

Принцип 6. Баланс работы и отдыха

В Valve не поощряют переработки. Здесь это считается признаком неправильно организованного процесса и поводом к его пересмотру. Кроме этого, компания оплачивает путешествия своим сотрудникам и по максимуму обеспечивает комфорт в офисе:

Иногда может показаться, что все происходящее в офисе это сон наяву. Если однажды утром вы обнаружите, что идете по коридору с миской свежих фруктов и чашкой первоклассного кофе, сдаете вещи в стирку и проходите в массажный кабинет, не теряйте самообладания! Все это действительно предназначено для сотрудников.

Принцип 7. Поиск сотрудников важнее, чем воздух

Мы ищем людей, которые лучше нас.

Зачастую прием на работу сотрудников, уступающих по уровню нанимателю это защитная реакция на большой объем задач. В таких условиях прием на работу среднячков может показаться (в краткосрочной перспективе) более разумным ходом, чем отказ от приема вообще. Но это огромная ошибка. Всегда можно привлечь на временную работу людей, которые помогут справиться с трудностями, но снижать стандарты в поиске сотрудников не следует никогда.

Подбор подходящих сотрудников это самая важная вещь во вселенной. Важнее, чем воздух. Поэтому когда вы занимаетесь кадрами все остальные занятия просто ничтожны, и о них следует забыть!

В целом, при собеседовании кандидата полезно задать себе следующие вопросы:

  • Хотел бы я, чтобы этот человек был моим начальником?

  • Многому ли он меня научит?

  • Что будет, если этот человек начнет работать у наших конкурентов?

Принцип 8. Ищите Т-образных людей

В Valve ценят Т-образных людей. Такие люди являются одновременно и специалистами широкого профиля (разбираются сразу в нескольких основных областях горизонтальная перекладина буквы Т), и узконаправленными специалистами (в своей отрасли они лучшие из лучших вертикальная перекладина буквы Т).

Такой набор качеств ключевой фактор успеха в Valve: узконаправленный специалист плохо взаимодействует с другими сотрудниками. А работник-универсал, не разбирающийся глубоко в определённой области, практически не участвует в работе и как сотрудник едва полезен.

Принцип 9. Самофинансирование

Valve была основана без привлечения денег инвесторов, и все акции компании до сих пор распределяются исключительно между работающими сотрудниками. Маловероятно встретить их когда-нибудь их на фондовой бирже. Почти исключена и продажа Valve другой компании. По словам Ньюэлла, если дела пойдут плохо, Valve скорее будет распущена, чем продана.

Все права на интеллектуальную собственность также принадлежат Valve.

Valve это самофинансируемая компания. Ничто не мешает нам принимать собственные решения в отношении наших продуктов.

Слабые стороны Valve

Несмотря на впечатляющие результаты и революционный подход к менеджменту, Valve признает, что некоторые вопросы им пока даются тяжело:

  • Помощь новичкам в работе и наставничество

  • Распространение информации в компании

  • Поиск и прием на работу специалистов в совершенно новых сферах

  • Прогнозирование на период более 3-4 месяцев

  • Зачастую у нас не получается привлечь в компанию одаренных людей, которые предпочитают работать в рамках более традиционной структуры. Это неотъемлемая особенность модели нашей компании, и мы ничего не можем здесь изменить, но как минимум стоит признать, что это ограничение мы наложили на себя сами.

Вопросы за кадром

Интересно, как работают сотрудники поддерживающих подразделений Valve: бухгалтерия, склад, логистика. Вряд ли там возможно такое же свободолюбие, как в продуктовых командах. К сожалению, информацию по данному вопросу мне найти не удалось.

Также интересно почитать отзывы бывших сотрудников о работе в такой организации. В сети их немного: есть негативный отзыв бывшего сотрудника Valve Рича Гелдриха, но в его твитах не сказано, что речь идет именно о Valve, он пишет о самоорганизующихся компаниях.

На сайте по поиску работу Indeed всего три отзыва о Valve, и все они неплохие, средний бал 4,3:


Источники:

1. Официальный сайт Valve

2. Руководство для новых сотрудников на русском и на английском

3. Статья в Нью-Йорк Таймс

4. Статья по мотивам твитов об ужасах "самоорганизующихся компаний" бывшего сотрудника

5. Страничка Indeed с отзывами на Valve


Для тех, кто предпочитает короткий жанр, автор ведет телеграм-канал@annakopyrneva, где пишет о софтскиллах, продукте и своей работе в ИТ.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru