This repository has been archived on 2026-07-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mam-migration/AH MAM Migration/src/com/bayer/edam/migration/MamMigrationSelective.java
T
2017-11-02 11:23:16 +01:00

598 lines
23 KiB
Java

package com.bayer.edam.migration;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import com.bayer.edam.migration.adam.Campaign;
import com.bayer.edam.migration.adam.Classification;
import com.bayer.edam.migration.adam.ClassificationList;
import com.bayer.edam.migration.adam.EntityId;
import com.bayer.edam.migration.adam.Token;
import com.bayer.edam.migration.adam.UploadClassification;
import com.bayer.edam.migration.adam.UploadRecord;
import com.bayer.edam.migration.adam.UploadRecord.WriteClassification;
import com.bayer.edam.migration.interceptors.LoggingRequestInterceptor;
import com.bayer.edam.migration.mam.CategoryTree;
import com.bayer.edam.migration.mam.CategoryTree.Node;
import com.bayer.edam.migration.mam.Entry;
import com.bayer.edam.migration.mam.Entry.Download;
import com.fasterxml.jackson.databind.ObjectMapper;
@SpringBootApplication
@PropertySource("file:secret.properties")
public class MamMigrationSelective implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(MamMigrationSelective.class);
static final String adamHost = "https://edam-q.bayer.com:2015";
static final String mamHost = "https://service.media-assistant.animalhealth.bayer.com";
private List<Integer> entrySelection = new ArrayList<Integer>(Arrays.asList( 48147 /* 58443/*, 53787, 32427, 71725, 63080*/ )); // 72048, 60984
static final String categoryJson = "resources/2017-10-19_categories.json";
static final String categoryMapping = "resources/Media_Assistant_Categories_131017_EDITED_without_archive.csv";
@Value("${mam.auth}")
private String mamAuth;
@Value("${adam.auth}")
private String adamAuth;
RestTemplate restTemplate;
HttpHeaders adamHeaders, adamUploadHeaders, mamHeaders;
HttpEntity<String> adamHeadersEntity;
private SimpleDateFormat isoDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
private SimpleDateFormat stringDate = new SimpleDateFormat("yyyy-MM-dd");
private ObjectMapper jsonMapper = new ObjectMapper();
private List<Classification> campaignYears;
private List<Campaign> campaigns = new LinkedList<Campaign>();
private CategoryTree mamCategoryTree;
static final SimpleDateFormat filenameDate = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
static final File entryLog = new File("entry_" + filenameDate.format(new Date()) + ".log");
static final String fieldSeparator = "|~|";
static final String lineSeparator = "\r\n";
@Override
public void run(String... strings) throws Exception {
// XXX
// readEntryIDsFromFile();
log.info("Write log headers for " + entryLog.getAbsolutePath());
FileUtils.writeStringToFile(entryLog, "entryId" + fieldSeparator + "Location" + fieldSeparator + "Entry" + lineSeparator, Charset.forName("utf-8"));
log.info("Read categories");
mamCategoryTree = new CategoryTree(categoryJson, categoryMapping);
restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);
adamHeaders = createAdamHeaders();
adamHeadersEntity = new HttpEntity<String>("", adamHeaders);
adamUploadHeaders = createAdamHeaders();
adamUploadHeaders.set("Content-Type", "multipart/form-data");
adamUploadHeaders.set("Accept", "*/*");
mamHeaders = createMamHeaders();
log.info("Read campaigns");
campaignYears = restTemplate.exchange(adamHost + "/classification/" + Definitions.campaignRoot + "/children", HttpMethod.GET, adamHeadersEntity, ClassificationList.class).getBody().items;
for (Classification yearClassification : campaignYears) {
List<Classification> campaignClassifications = restTemplate.exchange(adamHost + "/classification/" + yearClassification.id + "/children", HttpMethod.GET, adamHeadersEntity, ClassificationList.class).getBody().items;
for (Classification campaignClassification : campaignClassifications) {
//log.debug(yearClassification.name + ": " + campaignClassification.name + ", " + campaignClassification.labels[0].value + ", " + campaignClassification.id);
Campaign campaign = new Campaign(campaignClassification.name, campaignClassification.labels[0].value, yearClassification.name);
campaign.guid = campaignClassification.id;
campaigns.add(campaign);
}
}
HttpEntity<String> entity = new HttpEntity<String>("", mamHeaders);
for (int id : entrySelection) {
// XXX
// if needed start from a certain ID
// if (id > 32954)
// continue;
log.info("GET entry " + id);
Entry entry = restTemplate.exchange(mamHost + "/rest/migration/entry/{id}", HttpMethod.GET, entity, Entry.class, id).getBody();
log.debug("Name: " + entry.name.get("en"));
// check exclusion criteria
if ((entry.stateId == 5 || entry.stateId == 4) && entry.fileName == null && entry.categories.size() == 0) {
log.info("RESULT Skipping entry " + entry.id + ": Status " + entry.stateId + ", no file attached, no category set");
continue;
}
else if (entry.stateId == 7) {
log.info("RESULT Skipping entry " + entry.id + ": Status " + entry.stateId + " (LOCKED_BY_ADMIN)");
continue;
}
else if (entry.stateId == 8) {
log.info("RESULT Skipping entry " + entry.id + ": Status " + entry.stateId + " (ASSIGNED_TO_INDEXATION)");
continue;
}
else if (entry.stateId == 10) {
log.info("RESULT Skipping entry " + entry.id + ": Status " + entry.stateId + " (ARCHIVED)");
continue;
}
postToAdam(entry);
}
invalidateAdamToken();
}
void postToAdam(Entry entry) throws Exception {
UploadRecord record = new UploadRecord();
String entryJson = jsonMapper.writeValueAsString(entry);
Set<String> keywords = new HashSet<String>();
// globally applicable classifications
record.addClassification("Migration");
record.addClassification("Country.Global");
record.addClassification("Agencies.Bayer AH");
record.addClassification("Usage.Restricted");
// media type
record.addClassification(Definitions.mediaTypes.get(entry.mediaType.id));
// process categories
if (entry.categories.size() == 0) {
log.info("RESULT Skipping entry " + entry.id + ": No category assigned, cannot determine business unit and brand");
return;
}
if (entry.categories.size() > 1)
log.warn("Entry " + entry.id + " has " + entry.categories.size() + " categories assigned");
for (int categoryId : entry.categories) {
log.debug("Category: " + categoryId);
Node node = mamCategoryTree.getNode(categoryId);
log.debug("Node: " + node);
List<Node> path = new LinkedList<Node>();
Node tmpNode = node;
while (tmpNode.id != CategoryTree.ROOT_NODE_ID) {
if (tmpNode.id == 0) {
log.info("RESULT Skipping entry " + entry.id + ": Node outside of category tree (" + node.toString() + ")");
return;
}
path.add(0, tmpNode);
tmpNode = tmpNode.parent;
}
for (Node n : path) {
if (n.action.equals("not migrated")) {
log.info("RESULT Skipping entry " + entry.id + " because of tag on (parent) category " + n.name + " (" + n.id + ")");
return;
}
if (n.action.equals("classify")) {
String[] parameters = n.parameter.split(",");
for (String p : parameters) {
// Status "Released" is weaker than the other statuses
if (p.startsWith("Status.")) {
if (p.equals("Status.Released") && record.hasClassificationStartingWith("Status.")) {
log.warn("Classification " + p + " not added, different status already set");
continue;
}
else {
record.removeClassificationsStartingWith("Status.");
}
}
// Business unit "General" is weaker than the other business units
if (p.startsWith("BusinessUnit.")) {
if (p.equals("BusinessUnit.General") && record.hasClassificationStartingWith("BusinessUnit.")) {
log.warn("Classification " + p + " not added, different business unit already set");
continue;
}
else {
record.removeClassificationsStartingWith("BusinessUnit.");
}
}
// Brands "General" and "Others" are weaker than the other brands
if (p.startsWith("Brand.")) {
if ((p.equals("Brand.General") || p.equals("Brand.CAP.Others") || p.equals("Brand.FAP.Others"))&& record.hasClassificationStartingWith("Brand.")) {
log.warn("Classification " + p + " not added, different brand already set");
continue;
}
else {
record.removeClassificationsStartingWith("Brand.");
}
}
if (p.startsWith("MediaType.")) {
record.removeClassificationsStartingWith("MediaType.");
}
record.addClassification(p);
}
}
else if (n.action.equals("brand")) {
record.removeClassificationsStartingWith("Brand.");
String brandClassificationId = "Brand." + n.name.trim();
record.addClassification(brandClassificationId);
}
else if (n.action.equals("campaign")) {
String campaignName = n.name;
String campaignLabel = n.name;
String campaignYear;
if (n.parameter != null && !n.parameter.trim().equals("")) {
campaignLabel = campaignLabel + " " + n.parameter;
campaignYear = n.parameter.substring(1, 5);
} else {
campaignYear = n.name.substring(0, 4);
}
try {
Integer.parseInt(campaignYear);
// check whether the year classification already exists
Classification yearClassification = null;
for (Classification c : campaignYears) {
if (c.name.equals(campaignYear)) {
yearClassification = c;
}
}
if (yearClassification == null) {
log.debug("Create campaign year " + campaignYear);
UploadClassification newYear = new UploadClassification(campaignYear, campaignYear);
HttpEntity<UploadClassification> httpEntity = new HttpEntity<UploadClassification>(newYear, adamHeaders);
try {
EntityId classificationId = restTemplate.exchange(adamHost + "/classifications", HttpMethod.POST, httpEntity, EntityId.class).getBody();
newYear.guid = classificationId.id;
// retrieve the newly created classification back
yearClassification = restTemplate.exchange(adamHost + "/classification/" + newYear.guid, HttpMethod.GET, adamHeadersEntity, Classification.class).getBody();
campaignYears.add(yearClassification);
} catch (HttpStatusCodeException hsce) {
log.error("Could not create campaign year '" + campaignYear + "' for entry " + entry.id + ": " + hsce.getStatusCode().toString());
log.error(hsce.getResponseBodyAsString());
}
}
// check whether campaign already exists
String campaignGuid = null;
for (Campaign c : campaigns) {
if (c.name.equals(campaignName)) {
log.debug("Add existing campaign " + campaignName);
campaignGuid = c.guid;
record.addClassification(new WriteClassification(campaignGuid, 1));
}
}
if (campaignGuid == null) {
log.info("Create campaign " + campaignYear + "/" + campaignName);
Campaign campaign = new Campaign(campaignName, campaignLabel, campaignYear);
HttpEntity<Campaign> httpEntity = new HttpEntity<Campaign>(campaign, adamHeaders);
try {
EntityId campaignId = restTemplate.exchange(adamHost + "/classifications", HttpMethod.POST, httpEntity, EntityId.class).getBody();
campaign.guid = campaignId.id;
record.addClassification(new WriteClassification(campaign.guid, 1));
campaigns.add(campaign);
}
catch (HttpStatusCodeException hsce) {
log.error("Could not create campaign '" + campaignName + "' for entry " + entry.id + ": " + hsce.getStatusCode().toString());
log.error(hsce.getResponseBodyAsString());
}
}
} catch (NumberFormatException nfe) {
log.error("Could not parse year from string " + campaignYear);
}
}
else if (n.action.equals("keyword")) {
log.debug("Adding keyword " + n.name.trim());
keywords.add(n.name.trim());
}
}
}
// Set Status.Draft for entries with stateId == 5 (Created) or 4 (Assigned)
if (entry.stateId == 4 || entry.stateId == 5) {
log.debug("Entry has status " + entry.stateId + ", setting record status to 'Draft'");
record.removeClassification("Status.Released");
record.addClassification("Status.Draft");
}
// Fields
if (entry.name.get("en") != null) {
record.addField("byr_Title", entry.name.get("en"));
}
else {
log.warn("Entry " + entry.id + ": No title");
record.addField("byr_Title", "No title");
}
if (entry.creationDate != null)
record.addField("byr_ah_deliveryDate", isoDate.format(entry.creationDate));
// Project lead
if (entry.user != null && entry.user.get("id") != null) {
if (Definitions.users.get(entry.user.get("id")) != null) {
record.addField("byr_ah_projectLead", Collections.singletonList(Definitions.users.get(entry.user.get("id"))));
record.addField("byr_ah_brandCommunicationRepresentative", Collections.singletonList(Definitions.users.get(entry.user.get("id"))));
}
else {
log.warn("Entry " + entry.id + ": MAM user " + entry.user.get("id") + " not available, assigning default user");
record.addField("byr_ah_projectLead", Collections.singletonList(Definitions.defaultProjectLead));
record.addField("byr_ah_brandCommunicationRepresentative", Collections.singletonList(Definitions.defaultProjectLead));
}
}
else {
log.warn("Entry " + entry.id + ": No user, assigning default user");
record.addField("byr_ah_projectLead", Collections.singletonList(Definitions.defaultProjectLead));
record.addField("byr_ah_brandCommunicationRepresentative", Collections.singletonList(Definitions.defaultProjectLead));
}
// Description / Additional Information
String description = entry.description.get("en");
if (description == null)
description = "";
else
description += "\n\n";
if (entry.comment.get("en") != null)
description += "Comment: " + entry.comment.get("en") + "\n\n";
String migrationDescription = "Migrated from Media Assistant ID " + entry.id;
if (entry.creationDate != null)
migrationDescription += "\nDate of Issue: " + stringDate.format(entry.creationDate);
if (entry.categories.size() == 1)
migrationDescription += "\nCategory:";
else
migrationDescription += "\nCategories:";
for (int categoryId : entry.categories) {
migrationDescription += "\n" + mamCategoryTree.getNode(categoryId).toString();
}
description += migrationDescription;
record.addField("byr_ah_description", description);
// Keywords
if (entry.keyword.get("en") != null && entry.keyword.get("en").length() > 0) {
keywords.addAll(Arrays.asList(entry.keyword.get("en").split("\\s*,\\s*")));
}
if (keywords.size() > 0) {
record.addField("byr_ah_keywords_tl", new ArrayList<String>(keywords));
}
// License Information
record.addField("byr_ah_licenseHolderList", Collections.singletonList(Definitions.defaultLicenseHolder));
record.addField("byr_ah_licenseAdditionalPermittedUse", Collections.singletonList(Definitions.defaultAdditionalPermittedUse));
if (entry.copyright != null && entry.copyright.organization != null) {
String licenseDescription = "";
if (entry.copyright.use != null)
licenseDescription += entry.copyright.use;
record.addField("byr_ah_licenseComments", licenseDescription);
// if license holder is not "Bayer Animal Health GmbH" put on review list
if (!entry.copyright.organization.get("id").equals("3740"))
log.warn("Entry " + entry.id + ": License Holder is not set to 'Bayer Animal Health GmbH' (organization 3740)");
}
else {
// if license information is missing put on review list
log.warn("Entry " + entry.id + ": Copyright information missing");
}
if (entry.gpc != null)
record.addField("byr_ah_approvalNumber",entry.gpc.gpcNumber);
// Create record
HttpEntity<UploadRecord> httpEntity = new HttpEntity<UploadRecord>(record, adamHeaders);
try {
// URI location = restTemplate.postForLocation(adamHost + "/records", httpEntity, UploadRecord.class);
// log.info("RESULT Entry " + entry.id + " migrated to record " + location);
// XXX
uploadFilesForEntry(entry);
// TODO process file upload errors
// FileUtils.writeStringToFile(entryLog, entry.id + fieldSeparator + location.toString() + fieldSeparator + entryJson + lineSeparator,
// Charset.forName("utf-8"), true);
}
catch (HttpStatusCodeException hsce) {
log.error("RESULT Skipping entry " + entry.id + ": Error while creating record " + hsce.getStatusCode().toString());
log.error(hsce.getResponseBodyAsString());
}
}
private void uploadFilesForEntry(Entry entry) {
List<Download> downloadSelection = new ArrayList<Download>();
// Select download: Download original only. If original is not available, download largest file
Download selectedDownload = null;
for (Download download : entry.downloadList) {
if (download.entryFileVariation.equals("ORIGINAL")) {
selectedDownload = download;
break;
}
if (selectedDownload == null) {
selectedDownload = download;
}
else if (selectedDownload.size < download.size) {
selectedDownload = download;
}
}
if (selectedDownload != null)
downloadSelection.add(selectedDownload);
for (Download download : entry.downloadListPublicFolder) {
downloadSelection.add(download);
}
// Clean download selection
HashSet<String> hrefs = new HashSet<String>();
List<Download> cleanedDownloads = new ArrayList<Download>();
for (Download download : downloadSelection) {
if (hrefs.add(download.href)) {
cleanedDownloads.add(download);
}
}
// Instead of downloading from MAM, take the pre-downloaded files from the harddrive
for (Download download : cleanedDownloads) {
String filenameToUse;
if (download.fileNameOnDisc != null)
filenameToUse = download.fileNameOnDisc;
else
filenameToUse = download.fileName;
String filepath = "E:\\" + entry.id + "\\" + filenameToUse;
File uploadFile = new File(filepath);
if (uploadFile.exists()) {
if (uploadFile.length() < 4000000000L) {
log.debug("filesize " + uploadFile.length() + " < 4 GB");
log.info("Uploading file " + filepath);
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new FileSystemResource(uploadFile));
String result = restTemplate.postForObject(adamHost + "/uploads",
new HttpEntity<MultiValueMap<String, Object>>(parameters, adamUploadHeaders), String.class);
// String answer = restTemplate.exchange(adamHost + "/uploads", HttpMethod.POST,
// new HttpEntity<MultiValueMap<String, Object>>(parameters, adamUploadHeaders), String.class).getBody();
log.info("Upload result: " + result);
}
else {
log.debug("filesize " + uploadFile.length() + " > 4 GB!!!1!");
}
}
else {
log.error("Could not find file on disk " + filepath);
}
}
}
private void readEntryIDsFromFile() throws IOException {
File entryIdListFile = new File("resources/entrylist_2017-10-25.txt");
log.debug("Reading entry IDs from " + entryIdListFile.getAbsolutePath());
List<String> entryIdList = FileUtils.readLines(entryIdListFile, Charset.forName("utf-8"));
entrySelection = new ArrayList<Integer>();
log.debug("Parsing " + entryIdList.size() + " IDs");
for (String s : entryIdList) {
entrySelection.add(Integer.parseInt(s));
}
}
private HttpHeaders createMamHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "application/json");
headers.add("Authorization", "Basic " + mamAuth);
headers.add("Content-Type", "application/json");
headers.add("From", "migration@bayer.com");
return headers;
}
private HttpHeaders createAdamHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Registration", "ADAMBAYER");
headers.add("API-VERSION", "1");
headers.add("Accept", "application/hal+json");
headers.add("Content-Type", "application/json");
headers.add("Authorization", "Basic " + adamAuth);
headers.add("set-immediateSearchIndexUpdate", "true"); // see https://edam.bayer.com:2015/docs/resources/record#example-records-direct-push
HttpEntity<String> entity = new HttpEntity<String>("", headers);
Token token = restTemplate.exchange(adamHost + "/auth", HttpMethod.GET, entity, Token.class).getBody();
// replace auth header
headers.remove("Authorization");
headers.add("Authorization", "Token " + token.getToken());
// replace accept-charset header (in order to declutter log)
headers.add("Accept-Charset", "us-ascii, utf-16, utf-8");
return headers;
}
private void invalidateAdamToken() {
ResponseEntity<String> response = restTemplate.exchange(adamHost + "/auth", HttpMethod.DELETE, adamHeadersEntity, String.class);
log.info("Logged out from adam API: " + response.toString());
}
/*
* // Quoted "Z" to indicate UTC, no timezone offset private static SimpleDateFormat outputDateFormat = new
* SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); private static SimpleDateFormat inputDateFormat = new SimpleDateFormat("yyyy-MM-dd");
*
* public static String toIso8601(String isoDate) { try { return outputDateFormat.format(inputDateFormat.parse(isoDate)); } catch
* (ParseException e) { log.error(e.toString()); e.printStackTrace(); } return null; }
*/
public static void main(String args[]) {
SpringApplication.run(MamMigrationSelective.class, args);
}
}